feat: support lark document

--story=1017908 --user=王孝刚 【知识库】 - X-Pack支持对接飞书文档 https://www.tapd.cn/57709429/s/1673069
This commit is contained in:
wxg0103 2025-03-20 10:05:32 +08:00
parent e995a663c8
commit ea0ab5e3d2
14 changed files with 565 additions and 138 deletions

View File

@ -4,18 +4,19 @@ import type { datasetData } from '@/api/type/dataset'
import type { pageRequest } from '@/api/type/common'
import type { ApplicationFormType } from '@/api/type/application'
import { type Ref } from 'vue'
const prefix = '/dataset'
/**
*
* @param
* @param
* page {
"current_page": "string",
"page_size": "string",
}
"current_page": "string",
"page_size": "string",
}
* param {
"name": "string",
}
"name": "string",
}
*/
const getDataset: (
page: pageRequest,
@ -46,28 +47,28 @@ const delDataset: (dataset_id: String, loading?: Ref<boolean>) => Promise<Result
/**
*
* @param
* @param
* {
"name": "string",
"desc": "string",
"documents": [
{
"name": "string",
"paragraphs": [
{
"content": "string",
"title": "string",
"problem_list": [
{
"id": "string",
"content": "string"
}
]
}
]
}
]
}
"name": "string",
"desc": "string",
"documents": [
{
"name": "string",
"paragraphs": [
{
"content": "string",
"title": "string",
"problem_list": [
{
"id": "string",
"content": "string"
}
]
}
]
}
]
}
*/
const postDataset: (data: datasetData, loading?: Ref<boolean>) => Promise<Result<any>> = (
data,
@ -78,13 +79,13 @@ const postDataset: (data: datasetData, loading?: Ref<boolean>) => Promise<Result
/**
* Web知识库
* @param
* @param
* {
"name": "string",
"desc": "string",
"source_url": "string",
"selector": "string",
}
"name": "string",
"desc": "string",
"source_url": "string",
"selector": "string",
}
*/
const postWebDataset: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
data,
@ -92,15 +93,32 @@ const postWebDataset: (data: any, loading?: Ref<boolean>) => Promise<Result<any>
) => {
return post(`${prefix}/web`, data, undefined, loading)
}
/**
* Lark知识库
* @param
* {
"name": "string",
"desc": "string",
"app_id": "string",
"app_secret": "string",
"folder_token": "string",
}
*/
const postLarkDataset: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
data,
loading
) => {
return post(`${prefix}/lark/save`, data, undefined, loading)
}
/**
* QA知识库
* @param formData
* @param formData
* {
"file": "file",
"name": "string",
"desc": "string",
}
"file": "file",
"name": "string",
"desc": "string",
}
*/
const postQADataset: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
data,
@ -122,12 +140,12 @@ const getDatasetDetail: (dataset_id: string, loading?: Ref<boolean>) => Promise<
/**
*
* @param
* @param
* dataset_id
* {
"name": "string",
"desc": true
}
"name": "string",
"desc": true
}
*/
const putDataset: (
dataset_id: string,
@ -136,6 +154,13 @@ const putDataset: (
) => Promise<Result<any>> = (dataset_id, data, loading) => {
return put(`${prefix}/${dataset_id}`, data, undefined, loading)
}
const putLarkDataset: (
dataset_id: string,
data: any,
loading?: Ref<boolean>
) => Promise<Result<any>> = (dataset_id, data, loading) => {
return put(`${prefix}/lark/${dataset_id}`, data, undefined, loading)
}
/**
*
* @param dataset_id
@ -229,6 +254,29 @@ const getDatasetModel: (
) => Promise<Result<Array<any>>> = (dataset_id, loading) => {
return get(`${prefix}/${dataset_id}/model`, loading)
}
/**
*
* @param dataset_id
* @param folder_token
* @param loading
* @returns
*/
const getLarkDocumentList: (
dataset_id: string,
folder_token: string,
data: any,
loading?: Ref<boolean>
) => Promise<Result<Array<any>>> = (dataset_id, folder_token, data, loading) => {
return post(`${prefix}/lark/${dataset_id}/${folder_token}/doc_list`, data, null, loading)
}
const importLarkDocument: (
dataset_id: string,
data: any,
loading?: Ref<boolean>
) => Promise<Result<Array<any>>> = (dataset_id, data, loading) => {
return post(`${prefix}/lark/${dataset_id}/import`, data, null, loading)
}
export default {
getDataset,
@ -245,5 +293,9 @@ export default {
postQADataset,
exportDataset,
getDatasetModel,
exportZipDataset
exportZipDataset,
postLarkDataset,
getLarkDocumentList,
importLarkDocument,
putLarkDataset
}

View File

@ -181,6 +181,18 @@ const putDocumentSync: (
) => Promise<Result<any>> = (dataset_id, document_id, loading) => {
return put(`${prefix}/${dataset_id}/document/${document_id}/sync`, undefined, undefined, loading)
}
const putLarkDocumentSync: (
dataset_id: string,
document_id: string,
loading?: Ref<boolean>
) => Promise<Result<any>> = (dataset_id, document_id, loading) => {
return put(
`${prefix}/lark/${dataset_id}/document/${document_id}/sync`,
undefined,
undefined,
loading
)
}
/**
*
@ -193,6 +205,13 @@ const delMulSyncDocument: (
) => Promise<Result<boolean>> = (dataset_id, data, loading) => {
return put(`${prefix}/${dataset_id}/document/_bach`, { id_list: data }, undefined, loading)
}
const delMulLarkSyncDocument: (
dataset_id: string,
data: any,
loading?: Ref<boolean>
) => Promise<Result<boolean>> = (dataset_id, data, loading) => {
return put(`${prefix}/lark/${dataset_id}/_batch`, { id_list: data }, undefined, loading)
}
/**
* Web站点文档
@ -394,5 +413,7 @@ export default {
batchGenerateRelated,
cancelTask,
exportDocumentZip,
batchCancelTask
batchCancelTask,
putLarkDocumentSync,
delMulLarkSyncDocument
}

View File

@ -210,7 +210,8 @@ export default {
appSecretPlaceholder: 'Please enter APP secret',
verificationTokenPlaceholder: 'Please enter verification token',
urlInfo:
'-Events and callbacks - event configuration - configure the "request address" of the subscription method'
'-Events and callbacks - event configuration - configure the "request address" of the subscription method',
folderTokenPlaceholder: 'Please enter folder token'
},
slackSetting: {
title: 'Slack Configuration',

View File

@ -49,13 +49,21 @@ export default {
datasetType: {
label: 'Type',
generalInfo: 'Upload local documents',
webInfo: 'Sync text data from a web site'
webInfo: 'Sync text data from a web site',
larkInfo: 'Sync documents from Feishu',
yuqueInfo: 'Sync documents from Yuque'
},
source_url: {
label: 'Web Root URL',
placeholder: 'Please enter the web root URL',
requiredMessage: 'Please enter the web root URL'
},
user_id: {
requiredMessage: 'Please enter User ID'
},
token: {
requiredMessage: 'Please enter Token'
},
selector: {
label: 'Selector',
placeholder: 'Default is body, can input .classname/#idname/tagname'
@ -79,8 +87,7 @@ export default {
replace: 'Replace Sync',
replaceText: 'Re-fetch Web site documents, replacing the documents in the local knowledge',
complete: 'Full Sync',
completeText:
'Delete all documents in the local knowledge and re-fetch web site documents',
completeText: 'Delete all documents in the local knowledge and re-fetch web site documents',
tip: 'Note: All syncs will delete existing data and re-fetch new data. Please proceed with caution.'
}
}

View File

@ -197,7 +197,8 @@ export default {
appIdPlaceholder: '请输入App ID',
appSecretPlaceholder: '请输入App Secret',
verificationTokenPlaceholder: '请输入Verification Token',
urlInfo: '-事件与回调-事件配置-配置订阅方式的 "请求地址" 中'
urlInfo: '-事件与回调-事件配置-配置订阅方式的 "请求地址" 中',
folderTokenPlaceholder: '请输入Folder Token'
},
slackSetting: {
title: 'Slack 应用配置',

View File

@ -3,6 +3,8 @@ export default {
createDataset: '创建知识库',
general: '通用型',
web: 'web 站点',
lark: '飞书',
yuque: '语雀',
relatedApplications: '关联应用',
document_count: '文档数',
relatedApp_count: '关联应用',
@ -47,14 +49,22 @@ export default {
},
datasetType: {
label: '知识库类型',
generalInfo: '上传本地文档',
webInfo: '同步Web网站文本数据'
generalInfo: '通过上传文件或手动录入构建知识库',
webInfo: '通过网站链接构建知识库',
larkInfo: '通过飞书文档构建知识库',
yuqueInfo: '通过语雀文档构建知识库'
},
source_url: {
label: 'Web 根地址',
placeholder: '请输入 Web 根地址',
requiredMessage: ' 请输入 Web 根地址'
},
user_id: {
requiredMessage: '请输入User ID'
},
token: {
requiredMessage: '请输入Token'
},
selector: {
label: '选择器',
placeholder: '默认为 body可输入 .classname/#idname/tagname'

View File

@ -55,7 +55,8 @@ export default {
tip1: '1、点击下载对应模版并完善信息',
tip2: '2、上传的表格文件中每个 sheet 会作为一个文档sheet名称为文档名称',
tip3: '3、每次最多上传 50 个文件,每个文件不超过 100MB'
}
},
lark: {}
},
setRules: {
title: {

View File

@ -59,8 +59,8 @@ export default {
references: ' (引用知識庫)',
placeholder: '請輸入提示詞',
requiredMessage: '請輸入提示詞',
tooltip:'透過調整提示詞內容,可以引導大模型對話方向,該提示詞會被固定在上下文的開頭。',
tooltip: '透過調整提示詞內容,可以引導大模型對話方向,該提示詞會被固定在上下文的開頭。',
noReferencesTooltip:
'透過調整提示詞內容,可以引導大模型對話方向,該提示詞會被固定在上下文的開頭。可以使用變數:{question} 是用戶提出問題的佔位符。',
referencesTooltip:
@ -104,9 +104,9 @@ export default {
},
reasoningContent: {
label: '輸出思考',
tooltip:'請根據模型返回的思考標簽設置,標簽中間的內容將會認定爲思考過程',
tooltip: '請根據模型返回的思考標簽設置,標簽中間的內容將會認定爲思考過程',
start: '開始',
end: '結束',
end: '結束'
}
},
buttons: {
@ -196,12 +196,13 @@ export default {
appIdPlaceholder: '請輸入App ID',
appSecretPlaceholder: '請輸入App Secret',
verificationTokenPlaceholder: '請輸入Verification Token',
urlInfo: '-事件與回呼-事件配置-配置訂閱方式的 "請求位址" 中'
urlInfo: '-事件與回呼-事件配置-配置訂閱方式的 "請求位址" 中',
folderTokenPlaceholder: '請輸入Folder Token'
},
slackSetting: {
title: 'Slack 應用配置',
signingSecretPlaceholder: '請輸入 Signing Secret',
botUserTokenPlaceholder: '請輸入 Bot User Token',
botUserTokenPlaceholder: '請輸入 Bot User Token'
},
copyUrl: '複製連結填入到'
},

View File

@ -3,6 +3,8 @@ export default {
createDataset: '建立知識庫',
general: '通用型',
web: 'Web 站點',
lark: '飛書',
yuque: '語雀',
relatedApplications: '關聯應用',
document_count: '文檔數',
relatedApp_count: '關聯應用',
@ -46,14 +48,22 @@ export default {
},
datasetType: {
label: '知識庫類型',
generalInfo: '上傳本地檔案',
webInfo: '同步Web網站文字資料'
generalInfo: '透過上傳檔案或手動錄入建置知識庫',
webInfo: '透過網站連結建立知識庫',
larkInfo: '透過飛書文檔建構知識庫',
yuqueInfo: '透過語雀文件建構知識庫'
},
source_url: {
label: 'Web 根位址',
placeholder: '請輸入 Web 根位址',
requiredMessage: '請輸入 Web 根位址'
},
user_id: {
requiredMessage: '請輸入 User ID'
},
token: {
requiredMessage: '請輸入 Token'
},
selector: {
label: '選擇器',
placeholder: '預設為 body可輸入 .classname/#idname/tagname'

View File

@ -23,9 +23,9 @@
</AppAvatar>
<div>
<div>{{ $t('views.dataset.general') }}</div>
<el-text type="info">{{
$t('views.dataset.datasetForm.form.datasetType.generalInfo')
}}</el-text>
<el-text type="info"
>{{ $t('views.dataset.datasetForm.form.datasetType.generalInfo') }}
</el-text>
</div>
</div>
</el-card>
@ -42,6 +42,21 @@
</div>
</div>
</el-card>
<el-card shadow="never" class="mb-8" style="width: 50%" v-if="detail?.type === '2'">
<div class="flex align-center">
<AppAvatar shape="square" :size="32" style="background: none">
<img src="@/assets/logo_lark.svg" style="width: 100%" alt="" />
</AppAvatar>
<div>
<p>
<el-text>{{ $t('views.dataset.lark') }}</el-text>
</p>
<el-text type="info"
>{{ $t('views.dataset.datasetForm.form.datasetType.larkInfo') }}
</el-text>
</div>
</div>
</el-card>
</el-form-item>
<el-form-item
:label="$t('views.dataset.datasetForm.form.source_url.label')"
@ -64,6 +79,30 @@
@blur="form.selector = form.selector.trim()"
/>
</el-form-item>
<el-form-item label="App ID" prop="app_id" v-if="detail.type === '2'">
<el-input
v-model="form.app_id"
:placeholder="
$t('views.application.applicationAccess.larkSetting.appIdPlaceholder')
"
/>
</el-form-item>
<el-form-item label="App Secret" prop="app_id" v-if="detail.type === '2'">
<el-input
v-model="form.app_secret"
:placeholder="
$t('views.application.applicationAccess.larkSetting.appSecretPlaceholder')
"
/>
</el-form-item>
<el-form-item label="Folder Token" prop="folder_token" v-if="detail.type === '2'">
<el-input
v-model="form.folder_token"
:placeholder="
$t('views.application.applicationAccess.larkSetting.folderTokenPlaceholder')
"
/>
</el-form-item>
</el-form>
<div v-if="application_id_list.length > 0">
<h4 class="title-decoration-1 mb-16">{{ $t('views.dataset.relatedApplications') }}</h4>
@ -105,7 +144,7 @@
</div>
<div class="text-right">
<el-button @click="submit" type="primary"> {{ $t('common.save') }} </el-button>
<el-button @click="submit" type="primary"> {{ $t('common.save') }}</el-button>
</div>
</div>
</el-scrollbar>
@ -122,6 +161,7 @@ import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { isAppIcon } from '@/utils/application'
import useStore from '@/stores'
import { t } from '@/locales'
const route = useRoute()
const {
params: { id }
@ -138,7 +178,10 @@ const cloneModelId = ref('')
const form = ref<any>({
source_url: '',
selector: ''
selector: '',
app_id: '',
app_secret: '',
folder_token: ''
})
const rules = reactive({
@ -148,6 +191,27 @@ const rules = reactive({
message: t('views.dataset.datasetForm.form.source_url.requiredMessage'),
trigger: 'blur'
}
],
app_id: [
{
required: true,
message: t('views.application.applicationAccess.larkSetting.appIdPlaceholder'),
trigger: 'blur'
}
],
app_secret: [
{
required: true,
message: t('views.application.applicationAccess.larkSetting.appSecretPlaceholder'),
trigger: 'blur'
}
],
folder_token: [
{
required: true,
message: t('views.application.applicationAccess.larkSetting.folderTokenPlaceholder'),
trigger: 'blur'
}
]
})
@ -156,7 +220,7 @@ async function submit() {
await webFormRef.value.validate((valid: any) => {
if (valid) {
const obj =
detail.value.type === '1'
detail.value.type === '1' || detail.value.type === '2'
? {
application_id_list: application_id_list.value,
meta: form.value,
@ -172,17 +236,33 @@ async function submit() {
confirmButtonText: t('views.dataset.setting.vectorization')
})
.then(() => {
datasetApi.putDataset(id, obj, loading).then((res) => {
datasetApi.putReEmbeddingDataset(id).then(() => {
MsgSuccess(t('common.saveSuccess'))
if (detail.value.type === '2') {
datasetApi.putLarkDataset(id, obj, loading).then((res) => {
datasetApi.putReEmbeddingDataset(id).then(() => {
MsgSuccess(t('common.saveSuccess'))
})
})
})
} else {
datasetApi.putDataset(id, obj, loading).then((res) => {
datasetApi.putReEmbeddingDataset(id).then(() => {
MsgSuccess(t('common.saveSuccess'))
})
})
}
})
.catch(() => {})
} else {
datasetApi.putDataset(id, obj, loading).then((res) => {
MsgSuccess(t('common.saveSuccess'))
})
if (detail.value.type === '2') {
datasetApi.putLarkDataset(id, obj, loading).then((res) => {
datasetApi.putReEmbeddingDataset(id).then(() => {
MsgSuccess(t('common.saveSuccess'))
})
})
} else {
datasetApi.putDataset(id, obj, loading).then((res) => {
MsgSuccess(t('common.saveSuccess'))
})
}
}
}
})
@ -193,7 +273,7 @@ function getDetail() {
dataset.asyncGetDatasetDetail(id, loading).then((res: any) => {
detail.value = res.data
cloneModelId.value = res.data?.embedding_mode_id
if (detail.value.type === '1') {
if (detail.value.type === '1' || detail.value.type === '2') {
form.value = res.data.meta
}
application_id_list.value = res.data?.application_id_list

View File

@ -19,9 +19,9 @@
>
<div class="mt-16 mb-16">
<el-radio-group v-model="form.fileType" class="app-radio-button-group">
<el-radio-button value="txt">{{
$t('views.document.fileType.txt.label')
}}</el-radio-button>
<el-radio-button value="txt"
>{{ $t('views.document.fileType.txt.label') }}
</el-radio-button>
</el-radio-group>
</div>
<div class="update-info flex p-8-12 border-r-4 mb-16">
@ -42,7 +42,14 @@
/>
</div>
<el-tree :props="props" :load="loadNode" lazy show-checkbox />
<el-tree
:props="props"
:load="loadNode"
lazy
show-checkbox
node-key="token"
ref="treeRef"
/>
</el-form>
</div>
</el-scrollbar>
@ -65,20 +72,28 @@ import documentApi from '@/api/document'
import { MsgConfirm, MsgSuccess } from '@/utils/message'
import { t } from '@/locales'
import type Node from 'element-plus/es/components/tree/src/model/node'
import dataset from '@/api/dataset'
interface Tree {
name: string
leaf?: boolean
}
const router = useRouter()
const route = useRoute()
const {
query: { id } // iddatasetIDid
query: { id, folder_token } // iddatasetIDid folder_tokentoken
} = route
const datasetId = id as string
const folderToken = folder_token as string
const loading = ref(false)
const disabled = ref(false)
const allCheck = ref(false)
const treeRef = ref<any>(null)
interface Tree {
name: string
leaf?: boolean
type: string
token: string
is_exist: boolean
}
const form = ref({
fileType: 'txt',
fileList: [] as any
@ -93,33 +108,47 @@ const rules = reactive({
const props = {
label: 'name',
children: 'zones',
isLeaf: 'leaf'
isLeaf: (data: any) => data.type !== 'folder',
disabled: (data: any) => data.is_exist
}
const loadNode = (node: Node, resolve: (data: Tree[]) => void) => {
if (node.level === 0) {
return resolve([{ name: 'region' }])
}
if (node.level > 1) return resolve([])
setTimeout(() => {
const data: Tree[] = [
{
name: 'leaf',
leaf: true
},
{
name: 'zone'
}
]
resolve(data)
}, 500)
const loadNode = (node: Node, resolve: (nodeData: Tree[]) => void) => {
console.log(node)
const token = node.level === 0 ? folderToken : node.data.token // 使 folder_token使 node.data.token
dataset
.getLarkDocumentList(datasetId, token, {}, loading)
.then((res) => {
const data: any = res.data
resolve(data.files as Tree[])
})
.catch((err) => {
console.error('Failed to load tree nodes:', err)
})
}
function submit() {
loading.value = true
// token
const checkedNodes = treeRef.value?.getCheckedNodes() || []
const newList = checkedNodes.map((node: any) => {
return {
name: node.name,
token: node.token,
type: node.type
}
})
dataset
.importLarkDocument(datasetId, newList, loading)
.then((res) => {
MsgSuccess(t('views.document.tip.importMessage'))
router.go(-1)
})
.catch((err) => {
console.error('Failed to load tree nodes:', err)
})
loading.value = false
}
function back() {
router.go(-1)
}
@ -131,6 +160,7 @@ function back() {
margin: 0 auto;
overflow: hidden;
}
&__footer {
padding: 16px 24px;
position: fixed;
@ -140,6 +170,7 @@ function back() {
width: 100%;
box-sizing: border-box;
}
.upload-document {
width: 70%;
margin: 0 auto;

View File

@ -70,6 +70,58 @@
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" v-hasPermission="new ComplexPermission([], ['x-pack'], 'OR')">
<el-col :span="12">
<el-card
shadow="never"
class="mb-16"
:class="datasetForm.type === '2' ? 'active' : ''"
@click="datasetForm.type = '2'"
>
<div class="flex-between">
<div class="flex align-center">
<AppAvatar shape="square" :size="32" style="background: none">
<img src="@/assets/logo_lark.svg" style="width: 100%" alt="" />
</AppAvatar>
<div>
<p>
<el-text>{{ $t('views.dataset.lark') }}</el-text>
</p>
<el-text type="info">{{
$t('views.dataset.datasetForm.form.datasetType.larkInfo')
}}</el-text>
</div>
</div>
<el-radio value="2" size="large" style="width: 16px"></el-radio>
</div>
</el-card>
</el-col>
<el-col :span="12">
<!-- <el-card-->
<!-- shadow="never"-->
<!-- class="mb-16"-->
<!-- :class="datasetForm.type === '3' ? 'active' : ''"-->
<!-- @click="datasetForm.type = '3'"-->
<!-- >-->
<!-- <div class="flex-between">-->
<!-- <div class="flex align-center">-->
<!-- <AppAvatar class="mr-8" :size="32">-->
<!-- <img src="@/assets/icon_web.svg" style="width: 100%" alt="" />-->
<!-- </AppAvatar>-->
<!-- <div>-->
<!-- <p>-->
<!-- <el-text>{{ $t('views.dataset.yuque') }}</el-text>-->
<!-- </p>-->
<!-- <el-text type="info">{{-->
<!-- $t('views.dataset.datasetForm.form.datasetType.yuqueInfo')-->
<!-- }}</el-text>-->
<!-- </div>-->
<!-- </div>-->
<!-- <el-radio value="3" size="large" style="width: 16px"></el-radio>-->
<!-- </div>-->
<!-- </el-card>-->
</el-col>
</el-row>
</el-radio-group>
</el-form-item>
<el-form-item
@ -93,6 +145,42 @@
@blur="datasetForm.selector = datasetForm.selector.trim()"
/>
</el-form-item>
<el-form-item label="App ID" prop="app_id" v-if="datasetForm.type === '2'">
<el-input
v-model="datasetForm.app_id"
:placeholder="$t('views.application.applicationAccess.larkSetting.appIdPlaceholder')"
/>
</el-form-item>
<el-form-item label="App Secret" prop="app_id" v-if="datasetForm.type === '2'">
<el-input
v-model="datasetForm.app_secret"
:placeholder="$t('views.application.applicationAccess.larkSetting.appSecretPlaceholder')"
/>
</el-form-item>
<el-form-item label="Folder Token" prop="folder_token" v-if="datasetForm.type === '2'">
<el-input
v-model="datasetForm.folder_token"
:placeholder="
$t('views.application.applicationAccess.larkSetting.folderTokenPlaceholder')
"
/>
</el-form-item>
<el-form-item label="User ID" prop="user_id" v-if="datasetForm.type === '3'">
<el-input
v-model="datasetForm.user_id"
:placeholder="
$t('views.application.applicationAccess.larkSetting.folderTokenPlaceholder')
"
/>
</el-form-item>
<el-form-item label="Token" prop="token" v-if="datasetForm.type === '3'">
<el-input
v-model="datasetForm.token"
:placeholder="
$t('views.application.applicationAccess.larkSetting.folderTokenPlaceholder')
"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
@ -113,6 +201,7 @@ import BaseForm from './BaseForm.vue'
import datasetApi from '@/api/dataset'
import { MsgSuccess, MsgAlert } from '@/utils/message'
import { t } from '@/locales'
import { ComplexPermission } from '@/utils/permission/type'
const emit = defineEmits(['refresh'])
const router = useRouter()
@ -125,7 +214,10 @@ const dialogVisible = ref<boolean>(false)
const datasetForm = ref<any>({
type: '0',
source_url: '',
selector: ''
selector: '',
app_id: '',
app_secret: '',
folder_token: ''
})
const rules = reactive({
@ -135,6 +227,41 @@ const rules = reactive({
message: t('views.dataset.datasetForm.form.source_url.requiredMessage'),
trigger: 'blur'
}
],
app_id: [
{
required: true,
message: t('views.application.applicationAccess.larkSetting.appIdPlaceholder'),
trigger: 'blur'
}
],
app_secret: [
{
required: true,
message: t('views.application.applicationAccess.larkSetting.appSecretPlaceholder'),
trigger: 'blur'
}
],
folder_token: [
{
required: true,
message: t('views.application.applicationAccess.larkSetting.folderTokenPlaceholder'),
trigger: 'blur'
}
],
user_id: [
{
required: true,
message: t('views.dataset.datasetForm.form.user_id.requiredMessage'),
trigger: 'blur'
}
],
token: [
{
required: true,
message: t('views.dataset.datasetForm.form.token.requiredMessage'),
trigger: 'blur'
}
]
})
@ -167,13 +294,20 @@ const submitHandle = async () => {
router.push({ path: `/dataset/${res.data.id}/document` })
emit('refresh')
})
} else {
} else if (datasetForm.value.type === '1') {
const obj = { ...BaseFormRef.value.form, ...datasetForm.value }
datasetApi.postWebDataset(obj, loading).then((res) => {
MsgSuccess(t('common.createSuccess'))
router.push({ path: `/dataset/${res.data.id}/document` })
emit('refresh')
})
} else if (datasetForm.value.type === '2') {
const obj = { ...BaseFormRef.value.form, ...datasetForm.value }
datasetApi.postLarkDataset(obj, loading).then((res) => {
MsgSuccess(t('common.createSuccess'))
router.push({ path: `/dataset/${res.data.id}/document` })
emit('refresh')
})
}
} else {
return false

View File

@ -79,6 +79,20 @@
style="height: 22px"
>{{ $t('views.dataset.web') }}</el-tag
>
<el-tag
class="purple-tag"
v-else-if="item.type === '2'"
type="warning"
style="height: 22px"
>{{ $t('views.dataset.lark') }}</el-tag
>
<el-tag
class="purple-tag"
v-else-if="item.type === '3'"
type="warning"
style="height: 22px"
>{{ $t('views.dataset.yuque') }}</el-tag
>
</div>
<template #footer>

View File

@ -8,17 +8,34 @@
v-if="datasetDetail.type === '0'"
type="primary"
@click="router.push({ path: '/dataset/upload', query: { id: id } })"
>{{ $t('views.document.uploadDocument') }}</el-button
>
<el-button v-if="datasetDetail.type === '1'" type="primary" @click="importDoc">{{
$t('views.document.importDocument')
}}</el-button>
>{{ $t('views.document.uploadDocument') }}
</el-button>
<el-button v-if="datasetDetail.type === '1'" type="primary" @click="importDoc"
>{{ $t('views.document.importDocument') }}
</el-button>
<el-button
@click="syncMulDocument"
:disabled="multipleSelection.length === 0"
v-if="datasetDetail.type === '1'"
>{{ $t('views.document.syncDocument') }}</el-button
>
>{{ $t('views.document.syncDocument') }}
</el-button>
<el-button
v-if="datasetDetail.type === '2'"
type="primary"
@click="
router.push({
path: '/dataset/import',
query: { id: id, folder_token: datasetDetail.meta.folder_token }
})
"
>{{ $t('views.document.importDocument') }}
</el-button>
<el-button
@click="syncLarkMulDocument"
:disabled="multipleSelection.length === 0"
v-if="datasetDetail.type === '2'"
>{{ $t('views.document.syncDocument') }}
</el-button>
<el-button @click="openDatasetDialog()" :disabled="multipleSelection.length === 0">
{{ $t('views.document.setting.migration') }}
</el-button>
@ -101,7 +118,9 @@
link
:type="filterMethod['status'] ? 'primary' : ''"
>
<el-icon><Filter /></el-icon>
<el-icon>
<Filter />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu style="width: 100px">
@ -109,20 +128,20 @@
:class="filterMethod['status'] ? '' : 'is-active'"
:command="beforeCommand('status', '')"
class="justify-center"
>{{ $t('views.document.table.all') }}</el-dropdown-item
>
>{{ $t('views.document.table.all') }}
</el-dropdown-item>
<el-dropdown-item
:class="filterMethod['status'] === State.SUCCESS ? 'is-active' : ''"
class="justify-center"
:command="beforeCommand('status', State.SUCCESS)"
>{{ $t('views.document.fileStatus.SUCCESS') }}</el-dropdown-item
>
>{{ $t('views.document.fileStatus.SUCCESS') }}
</el-dropdown-item>
<el-dropdown-item
:class="filterMethod['status'] === State.FAILURE ? 'is-active' : ''"
class="justify-center"
:command="beforeCommand('status', State.FAILURE)"
>{{ $t('views.document.fileStatus.FAILURE') }}</el-dropdown-item
>
>{{ $t('views.document.fileStatus.FAILURE') }}
</el-dropdown-item>
<el-dropdown-item
:class="
filterMethod['status'] === State.STARTED &&
@ -132,14 +151,14 @@
"
class="justify-center"
:command="beforeCommand('status', State.STARTED, TaskType.EMBEDDING)"
>{{ $t('views.document.fileStatus.EMBEDDING') }}</el-dropdown-item
>
>{{ $t('views.document.fileStatus.EMBEDDING') }}
</el-dropdown-item>
<el-dropdown-item
:class="filterMethod['status'] === State.PENDING ? 'is-active' : ''"
class="justify-center"
:command="beforeCommand('status', State.PENDING)"
>{{ $t('views.document.fileStatus.PENDING') }}</el-dropdown-item
>
>{{ $t('views.document.fileStatus.PENDING') }}
</el-dropdown-item>
<el-dropdown-item
:class="
filterMethod['status'] === State.STARTED &&
@ -149,8 +168,8 @@
"
class="justify-center"
:command="beforeCommand('status', State.STARTED, TaskType.GENERATE_PROBLEM)"
>{{ $t('views.document.fileStatus.GENERATE') }}</el-dropdown-item
>
>{{ $t('views.document.fileStatus.GENERATE') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -170,7 +189,9 @@
link
:type="filterMethod['is_active'] ? 'primary' : ''"
>
<el-icon><Filter /></el-icon>
<el-icon>
<Filter />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu style="width: 100px">
@ -178,20 +199,20 @@
:class="filterMethod['is_active'] === '' ? 'is-active' : ''"
:command="beforeCommand('is_active', '')"
class="justify-center"
>{{ $t('views.document.table.all') }}</el-dropdown-item
>
>{{ $t('views.document.table.all') }}
</el-dropdown-item>
<el-dropdown-item
:class="filterMethod['is_active'] === true ? 'is-active' : ''"
class="justify-center"
:command="beforeCommand('is_active', true)"
>{{ $t('views.document.enableStatus.enable') }}</el-dropdown-item
>
>{{ $t('views.document.enableStatus.enable') }}
</el-dropdown-item>
<el-dropdown-item
:class="filterMethod['is_active'] === false ? 'is-active' : ''"
class="justify-center"
:command="beforeCommand('is_active', false)"
>{{ $t('views.document.enableStatus.close') }}</el-dropdown-item
>
>{{ $t('views.document.enableStatus.close') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -218,7 +239,9 @@
link
:type="filterMethod['hit_handling_method'] ? 'primary' : ''"
>
<el-icon><Filter /></el-icon>
<el-icon>
<Filter />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu style="width: 150px">
@ -226,15 +249,15 @@
:class="filterMethod['hit_handling_method'] ? '' : 'is-active'"
:command="beforeCommand('hit_handling_method', '')"
class="justify-center"
>{{ $t('views.document.table.all') }}</el-dropdown-item
>
>{{ $t('views.document.table.all') }}
</el-dropdown-item>
<template v-for="(value, key) of hitHandlingMethod" :key="key">
<el-dropdown-item
:class="filterMethod['hit_handling_method'] === key ? 'is-active' : ''"
class="justify-center"
:command="beforeCommand('hit_handling_method', key)"
>{{ $t(value) }}</el-dropdown-item
>
>{{ $t(value) }}
</el-dropdown-item>
</template>
</el-dropdown-menu>
</template>
@ -342,7 +365,7 @@
</el-dropdown>
</span>
</div>
<div v-if="datasetDetail.type === '1'">
<div v-if="datasetDetail.type === '1' || datasetDetail.type === '2'">
<span class="mr-4">
<el-tooltip
effect="dark"
@ -478,6 +501,7 @@ import GenerateRelatedDialog from '@/components/generate-related-dialog/index.vu
import EmbeddingContentDialog from '@/views/document/component/EmbeddingContentDialog.vue'
import { TaskType, State } from '@/utils/status'
import { t } from '@/locales'
const router = useRouter()
const route = useRoute()
const {
@ -596,6 +620,7 @@ function beforeCommand(attr: string, val: any, task_type?: number) {
task_type
}
}
const cancelTask = (row: any, task_type: number) => {
documentApi.cancelTask(row.dataset_id, row.id, { type: task_type }).then(() => {
MsgSuccess(t('views.document.tip.sendMessage'))
@ -606,6 +631,7 @@ function importDoc() {
title.value = t('views.document.importDocument')
ImportDocumentDialogRef.value.open()
}
function settingDoc(row: any) {
title.value = t('common.setting')
ImportDocumentDialogRef.value.open(row)
@ -640,6 +666,28 @@ const closeInterval = () => {
}
function syncDocument(row: any) {
console.log('row', row)
if (row.type === '1') {
syncWebDocument(row)
} else {
syncLarkDocument(row)
}
}
function syncLarkDocument(row: any) {
MsgConfirm(t('views.document.sync.confirmTitle'), t('views.document.sync.confirmMessage1'), {
confirmButtonText: t('views.document.sync.label'),
confirmButtonClass: 'danger'
})
.then(() => {
documentApi.putLarkDocumentSync(id, row.id).then(() => {
getList()
})
})
.catch(() => {})
}
function syncWebDocument(row: any) {
if (row.meta?.source_url) {
MsgConfirm(t('views.document.sync.confirmTitle'), t('views.document.sync.confirmMessage1'), {
confirmButtonText: t('views.document.sync.label'),
@ -708,6 +756,19 @@ function syncMulDocument() {
})
}
function syncLarkMulDocument() {
const arr: string[] = []
multipleSelection.value.map((v) => {
if (v) {
arr.push(v.id)
}
})
documentApi.delMulLarkSyncDocument(id, arr, loading).then(() => {
MsgSuccess(t('views.document.sync.successMessage'))
getList()
})
}
function deleteMulDocument() {
MsgConfirm(
`${t('views.document.delete.confirmTitle1')} ${multipleSelection.value.length} ${t('views.document.delete.confirmTitle2')}`,
@ -801,6 +862,7 @@ function editName(val: string, id: string) {
function cellMouseEnter(row: any) {
currentMouseId.value = row.id
}
function cellMouseLeave() {
currentMouseId.value = null
}
@ -846,6 +908,7 @@ function refresh() {
}
const GenerateRelatedDialogRef = ref()
function openGenerateDialog(row?: any) {
const arr: string[] = []
if (row) {
@ -883,6 +946,7 @@ onBeforeUnmount(() => {
<style lang="scss" scoped>
.document-main {
box-sizing: border-box;
.mul-operation {
position: fixed;
margin-left: var(--sidebar-width);