feat(shared resources): Add new shared resources
parent
bba9e62d43
commit
399e00415c
|
|
@ -20,6 +20,7 @@
|
|||
"@logicflow/core": "^2.0.15",
|
||||
"@logicflow/extension": "^2.0.20",
|
||||
"@vavt/cm-extension": "^1.9.1",
|
||||
"@vueuse/core": "^13.3.0",
|
||||
"@wecom/jssdk": "^2.3.1",
|
||||
"axios": "^1.8.4",
|
||||
"cropperjs": "^2.0.0-rc.2",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,499 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get, post, del, put, exportExcel, exportFile } from '@/request/index'
|
||||
import type { Ref } from 'vue'
|
||||
import type { KeyValue } from '@/api/type/common'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
|
||||
const prefix = '/system/shared/knowledge'
|
||||
|
||||
/**
|
||||
* 文档分页列表
|
||||
* @param 参数 knowledge_id,
|
||||
* param {
|
||||
" name": "string",
|
||||
}
|
||||
*/
|
||||
|
||||
const getDocument: (
|
||||
knowledge_id: string,
|
||||
page: pageRequest,
|
||||
param: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, page, param, loading) => {
|
||||
return get(
|
||||
`${prefix}/${knowledge_id}/document/${page.current_page}/${page.page_size}`,
|
||||
param,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 文档详情
|
||||
* @param 参数 knowledge_id
|
||||
*/
|
||||
const getDocumentDetail: (knowledge_id: string, document_id: string) => Promise<Result<any>> = (
|
||||
knowledge_id,
|
||||
document_id,
|
||||
) => {
|
||||
return get(`${prefix}/${knowledge_id}/document/${document_id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改文档
|
||||
* @param 参数
|
||||
* knowledge_id, document_id,
|
||||
* {
|
||||
"name": "string",
|
||||
"is_active": true,
|
||||
"meta": {}
|
||||
}
|
||||
*/
|
||||
const putDocument: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, data: any, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/document/${document_id}`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文档
|
||||
* @param 参数 knowledge_id, document_id,
|
||||
*/
|
||||
const delDocument: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, document_id, loading) => {
|
||||
return del(`${prefix}/${knowledge_id}/document/${document_id}`, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量取消文档任务
|
||||
* @param 参数 knowledge_id, document_id,
|
||||
*{
|
||||
"id_list": [
|
||||
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
],
|
||||
"type": 0
|
||||
}
|
||||
*/
|
||||
|
||||
const putBatchCancelTask: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/document/cancel_task/_batch`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消文档任务
|
||||
* @param 参数 knowledge_id, document_id,
|
||||
*/
|
||||
const putCancelTask: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, document_id, data, loading) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/cancel_task`,
|
||||
data,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载原文档
|
||||
* @param 参数 knowledge_id
|
||||
*/
|
||||
const getDownloadSourceFile: (knowledge_id: string, document_id: string) => Promise<Result<any>> = (
|
||||
knowledge_id,
|
||||
document_id,
|
||||
) => {
|
||||
return get(`${prefix}/${knowledge_id}/document/${document_id}/download_source_file`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出文档
|
||||
* @param document_name 文档名称
|
||||
* @param knowledge_id 数据集id
|
||||
* @param document_id 文档id
|
||||
* @param loading 加载器
|
||||
* @returns
|
||||
*/
|
||||
const exportDocument: (
|
||||
document_name: string,
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<any> = (document_name, knowledge_id, document_id, loading) => {
|
||||
return exportExcel(
|
||||
document_name + '.xlsx',
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/export`,
|
||||
{},
|
||||
loading,
|
||||
)
|
||||
}
|
||||
/**
|
||||
* 导出文档
|
||||
* @param document_name 文档名称
|
||||
* @param knowledge_id 数据集id
|
||||
* @param document_id 文档id
|
||||
* @param loading 加载器
|
||||
* @returns
|
||||
*/
|
||||
const exportDocumentZip: (
|
||||
document_name: string,
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<any> = (document_name, knowledge_id, document_id, loading) => {
|
||||
return exportFile(
|
||||
document_name + '.zip',
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/export_zip`,
|
||||
{},
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新文档向量库
|
||||
* @param 参数
|
||||
* knowledge_id, document_id,
|
||||
* {
|
||||
"state_list": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
*/
|
||||
const putDocumentRefresh: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
state_list: Array<string>,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, state_list, loading) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/refresh`,
|
||||
{ state_list },
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步web站点类型
|
||||
* @param 参数
|
||||
* knowledge_id, document_id,
|
||||
*/
|
||||
const putDocumentSync: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, loading) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/sync`,
|
||||
undefined,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建批量文档
|
||||
* @param 参数
|
||||
{
|
||||
"name": "string",
|
||||
"paragraphs": [
|
||||
{
|
||||
"content": "string",
|
||||
"title": "string",
|
||||
"problem_list": [
|
||||
{
|
||||
"id": "string",
|
||||
"content": "string"
|
||||
}
|
||||
],
|
||||
"is_active": true
|
||||
}
|
||||
],
|
||||
"source_file_id": string
|
||||
}
|
||||
*/
|
||||
const postMulDocument: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/document/batch_create`, data, {}, loading, 1000 * 60 * 5)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除文档
|
||||
* @param 参数 knowledge_id,
|
||||
* {
|
||||
"id_list": [String]
|
||||
}
|
||||
*/
|
||||
const delMulDocument: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, data, loading) => {
|
||||
return del(
|
||||
`${prefix}/${knowledge_id}/document/bach_delete`,
|
||||
undefined,
|
||||
{ id_list: data },
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量关联
|
||||
* @param 参数 knowledge_id,
|
||||
{
|
||||
"document_id_list": [
|
||||
"string"
|
||||
],
|
||||
"model_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"prompt": "string",
|
||||
"state_list": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
*/
|
||||
const putBatchGenerateRelated: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/document/batch_generate_related`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量修改命中方式
|
||||
* @param knowledge_id 知识库id
|
||||
* @param data
|
||||
* {id_list:[],hit_handling_method:'directly_return|optimization',directly_return_similarity}
|
||||
* @param loading
|
||||
* @returns
|
||||
*/
|
||||
const putBatchEditHitHandling: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/document/batch_hit_handling`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量刷新文档向量库
|
||||
* @param knowledge_id 知识库id
|
||||
* @param data
|
||||
{
|
||||
"id_list": [
|
||||
"string"
|
||||
],
|
||||
"state_list": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
* @param loading
|
||||
* @returns
|
||||
*/
|
||||
const putBatchRefresh: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
stateList: Array<string>,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, data, stateList, loading) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/batch_refresh`,
|
||||
{ id_list: data, state_list: stateList },
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量同步文档
|
||||
* @param 参数 knowledge_id,
|
||||
*/
|
||||
const putMulSyncDocument: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/document/batch_sync`, { id_list: data }, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量迁移文档
|
||||
* @param 参数 knowledge_id,target_knowledge_id,
|
||||
|
||||
*/
|
||||
const putMigrateMulDocument: (
|
||||
knowledge_id: string,
|
||||
target_knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, target_knowledge_id, data, loading) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/migrate/${target_knowledge_id}`,
|
||||
data,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入QA文档
|
||||
* @param 参数
|
||||
* file
|
||||
}
|
||||
*/
|
||||
const postQADocument: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, data, loading) => {
|
||||
return post(`${prefix}/${knowledge_id}/document/qa`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 分段预览(上传文档)
|
||||
* @param 参数 file:file,limit:number,patterns:array,with_filter:boolean
|
||||
*/
|
||||
const postSplitDocument: (data: any, id:string) => Promise<Result<any>> = (data, id) => {
|
||||
return post(`${prefix}/${id}/document/split`, data, undefined, undefined, 1000 * 60 * 60)
|
||||
}
|
||||
|
||||
/**
|
||||
* 分段标识列表
|
||||
* @param loading 加载器
|
||||
* @returns 分段标识列表
|
||||
*/
|
||||
const listSplitPattern: (
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<KeyValue<string, string>>>> = (loading) => {
|
||||
return get(`${prefix}/document/split_pattern`, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入表格
|
||||
* @param 参数
|
||||
* file
|
||||
*/
|
||||
const postTableDocument: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, data, loading) => {
|
||||
return post(`${prefix}/${knowledge_id}/document/table`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得QA模版
|
||||
* @param 参数 fileName,type,
|
||||
*/
|
||||
const exportQATemplate: (fileName: string, type: string, loading?: Ref<boolean>) => void = (
|
||||
fileName,
|
||||
type,
|
||||
loading,
|
||||
) => {
|
||||
return exportExcel(fileName, `${prefix}/document/template/export`, { type }, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得table模版
|
||||
* @param 参数 fileName,type,
|
||||
*/
|
||||
const exportTableTemplate: (fileName: string, type: string, loading?: Ref<boolean>) => void = (
|
||||
fileName,
|
||||
type,
|
||||
loading,
|
||||
) => {
|
||||
return exportExcel(fileName, `${prefix}/document/table_template/export`, { type }, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Web站点文档
|
||||
* @param 参数
|
||||
* {
|
||||
"source_url_list": [
|
||||
"string"
|
||||
],
|
||||
"selector": "string"
|
||||
}
|
||||
}
|
||||
*/
|
||||
const postWebDocument: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, data, loading) => {
|
||||
return post(`${prefix}/${knowledge_id}/document/web`, data, undefined, loading)
|
||||
}
|
||||
|
||||
const getAllDocument: (knowledge_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
knowledge_id,
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/${knowledge_id}/document`, undefined, loading)
|
||||
}
|
||||
|
||||
const putLarkDocumentSync: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, loading) => {
|
||||
return put(
|
||||
`${prefix}/lark/${knowledge_id}/document/${document_id}/sync`,
|
||||
undefined,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
const delMulLarkSyncDocument: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/lark/${knowledge_id}/_batch`, { id_list: data }, undefined, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
getDocument,
|
||||
getDocumentDetail,
|
||||
putDocument,
|
||||
delDocument,
|
||||
putBatchCancelTask,
|
||||
putCancelTask,
|
||||
getDownloadSourceFile,
|
||||
exportDocument,
|
||||
exportDocumentZip,
|
||||
putDocumentRefresh,
|
||||
putDocumentSync,
|
||||
postMulDocument,
|
||||
delMulDocument,
|
||||
putBatchGenerateRelated,
|
||||
putBatchEditHitHandling,
|
||||
putBatchRefresh,
|
||||
putMulSyncDocument,
|
||||
putMigrateMulDocument,
|
||||
postQADocument,
|
||||
postSplitDocument,
|
||||
listSplitPattern,
|
||||
postTableDocument,
|
||||
exportQATemplate,
|
||||
exportTableTemplate,
|
||||
postWebDocument,
|
||||
|
||||
getAllDocument,
|
||||
putLarkDocumentSync,
|
||||
delMulLarkSyncDocument,
|
||||
}
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get, post, del, put, exportFile, exportExcel } from '@/request/index'
|
||||
import { type Ref } from 'vue'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
import type { knowledgeData } from '@/api/type/knowledge'
|
||||
|
||||
const prefix = '/system/shared'
|
||||
const workspace_id = localStorage.getItem('workspace_id') || 'default'
|
||||
const getSharedWorkspaceKnowledge: (loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/workspace/${workspace_id}/knowledge`, {}, loading)
|
||||
}
|
||||
|
||||
const getSharedWorkspaceKnowledgePage: (
|
||||
param: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (param: any, loading) => {
|
||||
return get(`${prefix}/workspace/${workspace_id}/knowledge`, param, loading)
|
||||
}
|
||||
|
||||
const getSharedAuthorizationKnowledgeGet: (
|
||||
knowledge_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, loading) => {
|
||||
return get(`${prefix}/knowledge/${knowledge_id}/authorization`, {}, loading)
|
||||
}
|
||||
|
||||
const getSharedAuthorizationKnowledgePost: (
|
||||
knowledge_id: string,
|
||||
param?: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, param, loading) => {
|
||||
return post(`${prefix}/knowledge/${knowledge_id}/authorization`, param, loading)
|
||||
}
|
||||
|
||||
const getSharedAuthorizationToolGet: (
|
||||
knowledge_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, loading) => {
|
||||
return get(`${prefix}/tool/${knowledge_id}/authorization`, {}, loading)
|
||||
}
|
||||
|
||||
const getSharedAuthorizationToolPost: (
|
||||
knowledge_id: string,
|
||||
param?: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, param, loading) => {
|
||||
return post(`${prefix}/tool/${knowledge_id}/authorization`, param, loading)
|
||||
}
|
||||
|
||||
const getSharedAuthorizationModelGet: (
|
||||
knowledge_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, loading) => {
|
||||
return get(`${prefix}/model/${knowledge_id}/authorization`, {}, loading)
|
||||
}
|
||||
|
||||
const getSharedAuthorizationModelPost: (
|
||||
knowledge_id: string,
|
||||
param?: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, param, loading) => {
|
||||
return post(`${prefix}/model/${knowledge_id}/authorization`, param, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得知识库文件夹列表
|
||||
* @params 参数
|
||||
* {folder_id: string,
|
||||
* name: string,
|
||||
* user_id: string,
|
||||
* desc: string,}
|
||||
*/
|
||||
const getKnowledgeByFolder: (data?: any, loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/knowledge`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 知识库分页列表
|
||||
* @param 参数
|
||||
* param {
|
||||
"folder_id": "string",
|
||||
"name": "string",
|
||||
"tool_type": "string",
|
||||
desc: string,
|
||||
}
|
||||
*/
|
||||
const getKnowledgeList: (
|
||||
page: pageRequest,
|
||||
param?: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (page, param, loading) => {
|
||||
return get(`${prefix}/knowledge/${page.current_page}/${page.page_size}`, param, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 知识库详情
|
||||
* @param 参数 knowledge_id
|
||||
*/
|
||||
const getKnowledgeDetail: (knowledge_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
knowledge_id,
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/knowledge/${knowledge_id}`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改知识库信息
|
||||
* @param 参数
|
||||
* knowledge_id
|
||||
* {
|
||||
"name": "string",
|
||||
"desc": true
|
||||
}
|
||||
*/
|
||||
const putKnowledge: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/knowledge/${knowledge_id}`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除知识库
|
||||
* @param 参数 knowledge_id
|
||||
*/
|
||||
const delKnowledge: (knowledge_id: String, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
|
||||
knowledge_id,
|
||||
loading,
|
||||
) => {
|
||||
return del(`${prefix}/${knowledge_id}`, undefined, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 向量化知识库
|
||||
* @param 参数 knowledge_id
|
||||
*/
|
||||
const putReEmbeddingKnowledge: (
|
||||
knowledge_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, loading) => {
|
||||
return put(`${prefix}/knowledge/${knowledge_id}/embedding`, undefined, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出知识库
|
||||
* @param knowledge_name 知识库名称
|
||||
* @param knowledge_id 知识库id
|
||||
* @returns
|
||||
*/
|
||||
const exportKnowledge: (
|
||||
knowledge_name: string,
|
||||
knowledge_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<any> = (knowledge_name, knowledge_id, loading) => {
|
||||
return exportExcel(
|
||||
knowledge_name + '.xlsx',
|
||||
`${prefix}/${knowledge_id}/knowledge/${knowledge_id}/export`,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
/**
|
||||
*导出Zip知识库
|
||||
* @param knowledge_name 知识库名称
|
||||
* @param knowledge_id 知识库id
|
||||
* @param loading 加载器
|
||||
* @returns
|
||||
*/
|
||||
const exportZipKnowledge: (
|
||||
knowledge_name: string,
|
||||
knowledge_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<any> = (knowledge_name, knowledge_id, loading) => {
|
||||
return exportFile(
|
||||
knowledge_name + '.zip',
|
||||
`${prefix}/${knowledge_id}/knowledge/${knowledge_id}/export_zip`,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成关联问题
|
||||
* @param knowledge_id 知识库id
|
||||
* @param data
|
||||
* @param loading
|
||||
* @returns
|
||||
*/
|
||||
const putGenerateRelated: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/generate_related`, data, null, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 命中测试列表
|
||||
* @param knowledge_id
|
||||
* @param loading
|
||||
* @query { query_text: string, top_number: number, similarity: number }
|
||||
* @returns
|
||||
*/
|
||||
const getKnowledgeHitTest: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, data, loading) => {
|
||||
return get(`${prefix}/${knowledge_id}/hit_test`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步知识库
|
||||
* @param 参数 knowledge_id
|
||||
* @query 参数 sync_type // 同步类型->replace:替换同步,complete:完整同步
|
||||
*/
|
||||
const putSyncWebKnowledge: (
|
||||
knowledge_id: string,
|
||||
sync_type: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, sync_type, loading) => {
|
||||
return put(`${prefix}/knowledge/${knowledge_id}/sync`, undefined, { sync_type }, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建知识库
|
||||
* @param 参数
|
||||
* {
|
||||
"name": "string",
|
||||
"folder_id": "string",
|
||||
"desc": "string",
|
||||
"embedding": "string"
|
||||
}
|
||||
*/
|
||||
const postKnowledge: (data: knowledgeData, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return post(`${prefix}/knowledge/base`, data, undefined, loading, 1000 * 60 * 5)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户可使用的向量化模型列表
|
||||
* @param application_id
|
||||
* @param loading
|
||||
* @query { query_text: string, top_number: number, similarity: number }
|
||||
* @returns
|
||||
*/
|
||||
const getKnowledgeEmdeddingModel: (
|
||||
knowledge_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, loading) => {
|
||||
return get(`${prefix}/${knowledge_id}/emdedding_model`, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户可使用的模型列表
|
||||
* @param application_id
|
||||
* @param loading
|
||||
* @query { query_text: string, top_number: number, similarity: number }
|
||||
* @returns
|
||||
*/
|
||||
const getKnowledgeModel: (
|
||||
knowledge_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, loading) => {
|
||||
return get(`${prefix}/${knowledge_id}/model`, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Web知识库
|
||||
* @param 参数
|
||||
* {
|
||||
"name": "string",
|
||||
"folder_id": "string",
|
||||
"desc": "string",
|
||||
"embedding": "string",
|
||||
"source_url": "string",
|
||||
"selector": "string"
|
||||
}
|
||||
*/
|
||||
const postWebKnowledge: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return post(`${prefix}/knowledge/web`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部知识库
|
||||
* @param 参数
|
||||
*/
|
||||
const getAllKnowledge: (loading?: Ref<boolean>) => Promise<Result<any[]>> = (loading) => {
|
||||
return get(`${prefix}/knowledge`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取飞书文档列表
|
||||
* @param knowledge_id
|
||||
* @param folder_token
|
||||
* @param loading
|
||||
* @returns
|
||||
*/
|
||||
const getLarkDocumentList: (
|
||||
knowledge_id: string,
|
||||
folder_token: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, folder_token, data, loading) => {
|
||||
return post(`${prefix}/lark/${knowledge_id}/${folder_token}/doc_list`, data, null, loading)
|
||||
}
|
||||
|
||||
const importLarkDocument: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<any>>> = (knowledge_id, data, loading) => {
|
||||
return post(`${prefix}/lark/${knowledge_id}/import`, data, null, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
getKnowledgeByFolder,
|
||||
getKnowledgeList,
|
||||
getKnowledgeDetail,
|
||||
putKnowledge,
|
||||
delKnowledge,
|
||||
putReEmbeddingKnowledge,
|
||||
exportKnowledge,
|
||||
exportZipKnowledge,
|
||||
putGenerateRelated,
|
||||
getKnowledgeHitTest,
|
||||
putSyncWebKnowledge,
|
||||
postKnowledge,
|
||||
getKnowledgeModel,
|
||||
postWebKnowledge,
|
||||
|
||||
getLarkDocumentList,
|
||||
importLarkDocument,
|
||||
getAllKnowledge,
|
||||
getSharedWorkspaceKnowledge,
|
||||
getSharedWorkspaceKnowledgePage,
|
||||
getSharedAuthorizationKnowledgeGet,
|
||||
getSharedAuthorizationKnowledgePost,
|
||||
getSharedAuthorizationToolGet,
|
||||
getSharedAuthorizationToolPost,
|
||||
getSharedAuthorizationModelGet,
|
||||
getSharedAuthorizationModelPost,
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get, post, del, put } from '@/request/index'
|
||||
import { type Ref } from 'vue'
|
||||
import type {
|
||||
ListModelRequest,
|
||||
Model,
|
||||
CreateModelRequest,
|
||||
EditModelRequest,
|
||||
} from '@/api/type/model'
|
||||
import type { FormField } from '@/components/dynamics-form/type'
|
||||
|
||||
const prefix = '/system/shared'
|
||||
|
||||
/**
|
||||
* 获得模型列表
|
||||
* @params 参数 name, model_type, model_name
|
||||
*/
|
||||
const getModel: (
|
||||
request?: ListModelRequest,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<Model>>> = (data, loading) => {
|
||||
return get(`${prefix}/model`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型参数表单
|
||||
* @param model_id 模型id
|
||||
* @param loading
|
||||
* @returns
|
||||
*/
|
||||
const getModelParamsForm: (
|
||||
model_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<FormField>>> = (model_id, loading) => {
|
||||
return get(`${prefix}/model/${model_id}/model_params_form`, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模型
|
||||
* @param request 请求对象
|
||||
* @param loading 加载器
|
||||
* @returns
|
||||
*/
|
||||
const createModel: (
|
||||
request: CreateModelRequest,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Model>> = (request, loading) => {
|
||||
return post(`${prefix}/model`, request, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改模型
|
||||
* @param request 請求對象
|
||||
* @param loading 加載器
|
||||
* @returns
|
||||
*/
|
||||
const updateModel: (
|
||||
model_id: string,
|
||||
request: EditModelRequest,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Model>> = (model_id, request, loading) => {
|
||||
return put(`${prefix}/model/${model_id}`, request, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改模型参数配置
|
||||
* @param request 請求對象
|
||||
* @param loading 加載器
|
||||
* @returns
|
||||
*/
|
||||
const updateModelParamsForm: (
|
||||
model_id: string,
|
||||
request: any[],
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Model>> = (model_id, request, loading) => {
|
||||
return put(`${prefix}/model/${model_id}/model_params_form`, request, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型详情根据模型id 包括认证信息
|
||||
* @param model_id 模型id
|
||||
* @param loading 加载器
|
||||
* @returns
|
||||
*/
|
||||
const getModelById: (model_id: string, loading?: Ref<boolean>) => Promise<Result<Model>> = (
|
||||
model_id,
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/model/${model_id}`, {}, loading)
|
||||
}
|
||||
/**
|
||||
* 获取模型信息不包括认证信息根据模型id
|
||||
* @param model_id 模型id
|
||||
* @param loading 加载器
|
||||
* @returns
|
||||
*/
|
||||
const getModelMetaById: (model_id: string, loading?: Ref<boolean>) => Promise<Result<Model>> = (
|
||||
model_id,
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/model/${model_id}/meta`, {}, loading)
|
||||
}
|
||||
/**
|
||||
* 暂停下载
|
||||
* @param model_id 模型id
|
||||
* @param loading 加载器
|
||||
* @returns
|
||||
*/
|
||||
const pauseDownload: (model_id: string, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
|
||||
model_id,
|
||||
loading,
|
||||
) => {
|
||||
return put(`${prefix}/model/${model_id}/pause_download`, undefined, {}, loading)
|
||||
}
|
||||
const deleteModel: (model_id: string, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
|
||||
model_id,
|
||||
loading,
|
||||
) => {
|
||||
return del(`${prefix}/model/${model_id}`, undefined, {}, loading)
|
||||
}
|
||||
export default {
|
||||
getModel,
|
||||
createModel,
|
||||
updateModel,
|
||||
deleteModel,
|
||||
getModelById,
|
||||
getModelMetaById,
|
||||
pauseDownload,
|
||||
getModelParamsForm,
|
||||
updateModelParamsForm,
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get, post, del, put } from '@/request/index'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
import type { Ref } from 'vue'
|
||||
const prefix = '/system/shared/knowledge'
|
||||
|
||||
/**
|
||||
* 创建段落
|
||||
* @param 参数
|
||||
* knowledge_id, document_id
|
||||
* {
|
||||
"content": "string",
|
||||
"title": "string",
|
||||
"is_active": true,
|
||||
"problem_list": [
|
||||
{
|
||||
"content": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
const postParagraph: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, data, loading) => {
|
||||
return post(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/paragraph`,
|
||||
data,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 段落列表
|
||||
* @param 参数 knowledge_id document_id
|
||||
* param {
|
||||
"title": "string",
|
||||
"content": "string",
|
||||
}
|
||||
*/
|
||||
const getParagraph: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
page: pageRequest,
|
||||
param: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, page, param, loading) => {
|
||||
return get(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/paragraph/${page.current_page}/${page.page_size}`,
|
||||
param,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改段落
|
||||
* @param 参数
|
||||
* knowledge_id, document_id, paragraph_id
|
||||
* {
|
||||
"content": "string",
|
||||
"title": "string",
|
||||
"is_active": true,
|
||||
"problem_list": [
|
||||
{
|
||||
"content": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
const putParagraph: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
paragraph_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, paragraph_id, data, loading) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/paragraph/${paragraph_id}`,
|
||||
data,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除段落
|
||||
* @param 参数 knowledge_id, document_id, paragraph_id
|
||||
*/
|
||||
const delParagraph: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
paragraph_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, document_id, paragraph_id, loading) => {
|
||||
return del(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/paragraph/${paragraph_id}`,
|
||||
undefined,
|
||||
{},
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 某段落问题列表
|
||||
* @param 参数 knowledge_id,document_id,paragraph_id
|
||||
*/
|
||||
const getParagraphProblem: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
paragraph_id: string,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, paragraph_id: string) => {
|
||||
return get(`${prefix}/${knowledge_id}/document/${document_id}/paragraph/${paragraph_id}/problem`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 给某段落创建问题
|
||||
* @param 参数
|
||||
* knowledge_id, document_id, paragraph_id
|
||||
* {
|
||||
content": "string"
|
||||
}
|
||||
*/
|
||||
const postParagraphProblem: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
paragraph_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, paragraph_id, data: any, loading) => {
|
||||
return post(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/paragraph/${paragraph_id}/problem`,
|
||||
data,
|
||||
{},
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加某段落关联问题
|
||||
* @param knowledge_id 数据集id
|
||||
* @param document_id 文档id
|
||||
* @param loading 加载器
|
||||
* @query data {
|
||||
* paragraph_id 段落id problem_id 问题id
|
||||
* }
|
||||
*/
|
||||
const putAssociationProblem: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, document_id, data, loading) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/paragraph/association`,
|
||||
{},
|
||||
data,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除段落
|
||||
* @param 参数 knowledge_id, document_id
|
||||
*/
|
||||
const putMulParagraph: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, document_id, data, loading) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/paragraph/batch_delete`,
|
||||
{ id_list: data },
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量关联问题
|
||||
* @param 参数 knowledge_id, document_id
|
||||
* {
|
||||
"paragraph_id_list": [
|
||||
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
],
|
||||
"model_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||
"prompt": "string",
|
||||
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
|
||||
}
|
||||
*/
|
||||
const putBatchGenerateRelated: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, document_id, data, loading) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/paragraph/batch_generate_related`,
|
||||
data,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量迁移段落
|
||||
* @param 参数 knowledge_id,target_knowledge_id,
|
||||
*/
|
||||
const putMigrateMulParagraph: (
|
||||
knowledge_id: string,
|
||||
document_id: string,
|
||||
target_knowledge_id: string,
|
||||
target_document_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (
|
||||
knowledge_id,
|
||||
document_id,
|
||||
target_knowledge_id,
|
||||
target_document_id,
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return put(
|
||||
`${prefix}/${knowledge_id}/document/${document_id}/paragraph/migrate/dataset/${target_knowledge_id}/document/${target_document_id}`,
|
||||
data,
|
||||
undefined,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除某段落关联问题
|
||||
* @param 参数 dataset_id, document_id,
|
||||
* @query data {
|
||||
* paragraph_id 段落id problem_id 问题id
|
||||
* }
|
||||
*/
|
||||
const putDisassociationProblem: (
|
||||
dataset_id: string,
|
||||
document_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (dataset_id, document_id, data, loading) => {
|
||||
return put(
|
||||
`${prefix}/${dataset_id}/document/${document_id}/paragraph/unassociation`,
|
||||
{},
|
||||
data,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
postParagraph,
|
||||
getParagraph,
|
||||
putParagraph,
|
||||
delParagraph,
|
||||
getParagraphProblem,
|
||||
postParagraphProblem,
|
||||
putAssociationProblem,
|
||||
putMulParagraph,
|
||||
putBatchGenerateRelated,
|
||||
putMigrateMulParagraph,
|
||||
putDisassociationProblem,
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get, post, del, put } from '@/request/index'
|
||||
import type { Ref } from 'vue'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
|
||||
const prefix = '/system/shared/knowledge'
|
||||
|
||||
/**
|
||||
* 创建问题
|
||||
* @param 参数 knowledge_id
|
||||
* data: array[string]
|
||||
*/
|
||||
const postProblems: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, data, loading) => {
|
||||
return post(`${prefix}/${knowledge_id}/problem`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 问题分页列表
|
||||
* @param 参数 knowledge_id,
|
||||
* query {
|
||||
"content": "string",
|
||||
}
|
||||
*/
|
||||
|
||||
const getProblems: (
|
||||
knowledge_id: string,
|
||||
page: pageRequest,
|
||||
param: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, page, param, loading) => {
|
||||
return get(
|
||||
`${prefix}/${knowledge_id}/problem/${page.current_page}/${page.page_size}`,
|
||||
param,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改问题
|
||||
* @param 参数
|
||||
* knowledge_id, problem_id,
|
||||
* {
|
||||
"content": "string",
|
||||
}
|
||||
*/
|
||||
const putProblems: (
|
||||
knowledge_id: string,
|
||||
problem_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, problem_id, data: any, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/problem/${problem_id}`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除问题
|
||||
* @param 参数 knowledge_id, problem_id,
|
||||
*/
|
||||
const delProblems: (
|
||||
knowledge_id: string,
|
||||
problem_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, problem_id, loading) => {
|
||||
return del(`${prefix}/${knowledge_id}/problem/${problem_id}`, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 问题详情
|
||||
* @param 参数
|
||||
* knowledge_id, problem_id,
|
||||
*/
|
||||
const getDetailProblems: (
|
||||
knowledge_id: string,
|
||||
problem_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (knowledge_id, problem_id, loading) => {
|
||||
return get(`${prefix}/${knowledge_id}/problem/${problem_id}/paragraph`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量关联段落
|
||||
* @param 参数 knowledge_id,
|
||||
* {
|
||||
"problem_id_list": "Array",
|
||||
"paragraph_list": "Array",
|
||||
}
|
||||
*/
|
||||
const putMulAssociationProblem: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/problem/batch_association`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除问题
|
||||
* @param 参数 knowledge_id,
|
||||
* data: array[string]
|
||||
*/
|
||||
const putMulProblem: (
|
||||
knowledge_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<boolean>> = (knowledge_id, data, loading) => {
|
||||
return put(`${prefix}/${knowledge_id}/problem/batch_delete`, data, undefined, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
postProblems,
|
||||
getProblems,
|
||||
putProblems,
|
||||
delProblems,
|
||||
getDetailProblems,
|
||||
putMulAssociationProblem,
|
||||
putMulProblem,
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
import {Result} from '@/request/Result'
|
||||
import {get, post} from '@/request/index'
|
||||
import type {Ref} from 'vue'
|
||||
import type {Provider, BaseModel} from '@/api/type/model'
|
||||
import type {FormField} from '@/components/dynamics-form/type'
|
||||
import type {KeyValue} from '../type/common'
|
||||
|
||||
const prefix_provider = '/provider'
|
||||
/**
|
||||
* 获得供应商列表
|
||||
*/
|
||||
const getProvider: (loading?: Ref<boolean>) => Promise<Result<Array<Provider>>> = (loading) => {
|
||||
return get(`${prefix_provider}`, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得供应商列表
|
||||
*/
|
||||
const getProviderByModelType: (
|
||||
model_type: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<Provider>>> = (model_type, loading) => {
|
||||
return get(`${prefix_provider}`, {model_type}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型创建表单
|
||||
* @param provider
|
||||
* @param model_type
|
||||
* @param model_name
|
||||
* @param loading
|
||||
* @returns
|
||||
*/
|
||||
const getModelCreateForm: (
|
||||
provider: string,
|
||||
model_type: string,
|
||||
model_name: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<FormField>>> = (provider, model_type, model_name, loading) => {
|
||||
return get(`${prefix_provider}/model_form`, {provider, model_type, model_name}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型类型列表
|
||||
* @param provider 供应商
|
||||
* @param loading 加载器
|
||||
* @returns 模型类型列表
|
||||
*/
|
||||
const listModelType: (
|
||||
provider: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<KeyValue<string, string>>>> = (provider, loading?: Ref<boolean>) => {
|
||||
return get(`${prefix_provider}/model_type_list`, {provider}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础模型列表
|
||||
* @param provider
|
||||
* @param model_type
|
||||
* @param loading
|
||||
* @returns
|
||||
*/
|
||||
const listBaseModel: (
|
||||
provider: string,
|
||||
model_type: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<BaseModel>>> = (provider, model_type, loading) => {
|
||||
return get(`${prefix_provider}/model_list`, {provider, model_type}, loading)
|
||||
}
|
||||
|
||||
const listBaseModelParamsForm: (
|
||||
provider: string,
|
||||
model_type: string,
|
||||
model_name: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<Array<BaseModel>>> = (provider, model_type, model_name, loading) => {
|
||||
return get(`${prefix_provider}/model_params_form`, {provider, model_type, model_name}, loading)
|
||||
}
|
||||
export default {
|
||||
getProvider,
|
||||
getModelCreateForm,
|
||||
getProviderByModelType,
|
||||
listModelType,
|
||||
listBaseModel,
|
||||
listBaseModelParamsForm,
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get, post, del, put, exportFile } from '@/request/index'
|
||||
import { type Ref } from 'vue'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
import type { toolData } from '@/api/type/tool'
|
||||
|
||||
const prefix = '/system/shared'
|
||||
|
||||
/**
|
||||
* 获得工具文件夹列表
|
||||
* @params 参数 {folder_id: string}
|
||||
*/
|
||||
const getToolByFolder: (data?: any, loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/tool`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具列表
|
||||
* @param 参数
|
||||
* param {
|
||||
"folder_id": "string",
|
||||
"name": "string",
|
||||
"tool_type": "string",
|
||||
}
|
||||
*/
|
||||
const getToolList: (
|
||||
page: pageRequest,
|
||||
param?: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (page, param, loading) => {
|
||||
return get(`${prefix}/tool/${page.current_page}/${page.page_size}`, param, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建工具
|
||||
* @param 参数
|
||||
*/
|
||||
const postTool: (data: toolData, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return post(`${prefix}/tool`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工具
|
||||
* @param 参数
|
||||
|
||||
*/
|
||||
const putTool: (tool_id: string, data: toolData, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
tool_id,
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return put(`${prefix}/tool/${tool_id}`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工具详情
|
||||
* @param tool_id 工具id
|
||||
* @param loading 加载器
|
||||
* @returns 函数详情
|
||||
*/
|
||||
const getToolById: (tool_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
tool_id,
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/tool/${tool_id}`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工具
|
||||
* @param 参数 tool_id
|
||||
*/
|
||||
const delTool: (tool_id: String, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
|
||||
tool_id,
|
||||
loading,
|
||||
) => {
|
||||
return del(`${prefix}/${tool_id}`, undefined, {}, loading)
|
||||
}
|
||||
|
||||
const putToolIcon: (id: string, data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
id,
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return put(`${prefix}/${id}/edit_icon`, data, undefined, loading)
|
||||
}
|
||||
|
||||
const exportTool = (id: string, name: string, loading?: Ref<boolean>) => {
|
||||
return exportFile(name + '.fx', `${prefix}/${id}/export`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试工具
|
||||
* @param 参数
|
||||
|
||||
*/
|
||||
const postToolDebug: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
data: any,
|
||||
loading,
|
||||
) => {
|
||||
return post(`${prefix}/debug`, data, undefined, loading)
|
||||
}
|
||||
|
||||
const postImportTool: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return post(`${prefix}/import`, data, undefined, loading)
|
||||
}
|
||||
|
||||
const postPylint: (code: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
code,
|
||||
loading,
|
||||
) => {
|
||||
return post(`${prefix}/tool/pylint`, { code }, {}, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
getToolByFolder,
|
||||
getToolList,
|
||||
putTool,
|
||||
getToolById,
|
||||
postTool,
|
||||
postToolDebug,
|
||||
postImportTool,
|
||||
postPylint,
|
||||
exportTool,
|
||||
putToolIcon,
|
||||
delTool,
|
||||
}
|
||||
|
|
@ -65,4 +65,116 @@ export default {
|
|||
])
|
||||
},
|
||||
},
|
||||
'right-outlined': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
viewBox: '0 0 12 12',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M8.13909 6L4.07322 1.93414C3.97559 1.83651 3.97559 1.67822 4.07322 1.58059L4.42678 1.22703C4.52441 1.1294 4.6827 1.1294 4.78033 1.22703L9.19975 5.64645C9.39501 5.84171 9.39501 6.15829 9.19975 6.35356L4.78033 10.773C4.6827 10.8706 4.52441 10.8706 4.42678 10.773L4.07322 10.4194C3.97559 10.3218 3.97559 10.1635 4.07322 10.0659L8.13909 6Z',
|
||||
fill: 'currentColor',
|
||||
}),
|
||||
],
|
||||
),
|
||||
])
|
||||
},
|
||||
},
|
||||
'app-folder-share-active': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
viewBox: '0 0 20 20',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M0.833334 3.33333C0.833334 2.8731 1.20643 2.5 1.66667 2.5H8.65164C8.96728 2.5 9.25583 2.67834 9.39699 2.96066L10 4.16667H18.3333C18.7936 4.16667 19.1667 4.53976 19.1667 5V16.6667C19.1667 17.1269 18.7936 17.5 18.3333 17.5H1.66667C1.20643 17.5 0.833334 17.1269 0.833334 16.6667V3.33333Z',
|
||||
fill: '#3370FF',
|
||||
}),
|
||||
h('path', {
|
||||
d: 'M10.5403 9.27428C10.505 9.15191 10.4861 9.02261 10.4861 8.88889C10.4861 8.12183 11.1079 7.5 11.875 7.5C12.6421 7.5 13.2639 8.12183 13.2639 8.88889C13.2639 9.65595 12.6421 10.2778 11.875 10.2778C11.492 10.2778 11.1451 10.1227 10.8939 9.87195L9.39028 10.7604C9.42555 10.8828 9.44444 11.0121 9.44444 11.1458C9.44444 11.2379 9.43549 11.3278 9.41841 11.4149L11.3337 12.3342C11.5885 12.0321 11.9697 11.8403 12.3958 11.8403C13.1629 11.8403 13.7847 12.4621 13.7847 13.2292C13.7847 13.9962 13.1629 14.6181 12.3958 14.6181C11.6288 14.6181 11.0069 13.9962 11.0069 13.2292C11.0069 13.1371 11.0159 13.0472 11.033 12.9601L9.11769 12.0408C8.86291 12.3429 8.48164 12.5347 8.05556 12.5347C7.28849 12.5347 6.66667 11.9129 6.66667 11.1458C6.66667 10.3788 7.28849 9.75694 8.05556 9.75694C8.43859 9.75694 8.78541 9.912 9.03667 10.1628L10.5403 9.27428Z',
|
||||
fill: 'white',
|
||||
}),
|
||||
],
|
||||
),
|
||||
])
|
||||
},
|
||||
},
|
||||
'app-folder-share': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
viewBox: '0 0 20 20',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M10.4015 9.13532C10.3663 9.01295 10.3474 8.88365 10.3474 8.74993C10.3474 7.98287 10.9692 7.36104 11.7363 7.36104C12.5033 7.36104 13.1251 7.98287 13.1251 8.74993C13.1251 9.51699 12.5033 10.1388 11.7363 10.1388C11.3532 10.1388 11.0064 9.98377 10.7551 9.733L9.25154 10.6215C9.2868 10.7439 9.3057 10.8732 9.3057 11.0069C9.3057 11.0989 9.29675 11.1889 9.27967 11.2759L11.195 12.1952C11.4497 11.8932 11.831 11.7013 12.2571 11.7013C13.0242 11.7013 13.646 12.3231 13.646 13.0902C13.646 13.8573 13.0242 14.4791 12.2571 14.4791C11.49 14.4791 10.8682 13.8573 10.8682 13.0902C10.8682 12.9982 10.8772 12.9082 10.8942 12.8212L8.97894 11.9019C8.72417 12.2039 8.3429 12.3958 7.91681 12.3958C7.14975 12.3958 6.52792 11.7739 6.52792 11.0069C6.52792 10.2398 7.14975 9.61799 7.91681 9.61799C8.29985 9.61799 8.64667 9.77304 8.89793 10.0238L10.4015 9.13532Z',
|
||||
fill: '#1F2329',
|
||||
}),
|
||||
h('path', {
|
||||
d: 'M0.833344 3.33333V16.6667C0.833344 17.1269 1.22421 17.5 1.70636 17.5H18.2937C18.7758 17.5 19.1667 17.1269 19.1667 16.6667V5C19.1667 4.53976 18.7758 4.16667 18.2937 4.16667H10L9.397 2.96066C9.25584 2.67834 8.96729 2.5 8.65165 2.5H1.66668C1.20644 2.5 0.833344 2.8731 0.833344 3.33333ZM2.50001 15.8333V5.83333H17.5V15.8333H2.50001Z',
|
||||
fill: '#1F2329',
|
||||
}),
|
||||
],
|
||||
),
|
||||
])
|
||||
},
|
||||
},
|
||||
'moments-categories': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
viewBox: '0 0 20 20',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M10.2239 2.22566L18.6144 7.56512C18.8717 7.72885 18.8717 8.10444 18.6144 8.26817L10.2239 13.6076C10.0874 13.6945 9.91296 13.6945 9.77647 13.6076L1.3859 8.26817C1.12861 8.10444 1.12861 7.72885 1.3859 7.56512L9.77647 2.22566C9.91296 2.13881 10.0874 2.13881 10.2239 2.22566ZM3.93789 7.91664L10.0002 11.7745L16.0625 7.91664L10.0002 4.05883L3.93789 7.91664Z',
|
||||
fill: '#1F2329',
|
||||
}),
|
||||
h('path', {
|
||||
d: 'M2.16387 11.5717C1.96769 11.4515 1.71114 11.513 1.59086 11.7092L1.15526 12.4196C1.03498 12.6158 1.0965 12.8723 1.29268 12.9926L8.92197 17.6705C9.45728 17.9987 10.1264 17.9987 10.6617 17.6705L18.291 12.9926C18.4871 12.8723 18.5487 12.6158 18.4284 12.4196L17.9928 11.7092C17.8725 11.513 17.6159 11.4515 17.4198 11.5717L9.90072 16.182C9.8339 16.223 9.74974 16.223 9.68292 16.182L2.16387 11.5717Z',
|
||||
fill: '#1F2329',
|
||||
}),
|
||||
],
|
||||
),
|
||||
])
|
||||
},
|
||||
},
|
||||
'close-outlined': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
viewBox: '0 0 16 16',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M8.00023 7.05718L12.4786 2.57883C12.6087 2.44866 12.8198 2.44866 12.95 2.57883L13.4214 3.05024C13.5516 3.18041 13.5516 3.39147 13.4214 3.52164L8.94304 7.99998L13.4214 12.4783C13.5516 12.6085 13.5516 12.8196 13.4214 12.9497L12.95 13.4211C12.8198 13.5513 12.6087 13.5513 12.4786 13.4211L8.00023 8.94279L3.52189 13.4211C3.39171 13.5513 3.18066 13.5513 3.05048 13.4211L2.57908 12.9497C2.4489 12.8196 2.4489 12.6085 2.57908 12.4783L7.05742 7.99998L2.57908 3.52164C2.4489 3.39147 2.4489 3.18041 2.57908 3.05024L3.05048 2.57883C3.18066 2.44866 3.39171 2.44866 3.52189 2.57883L8.00023 7.05718Z',
|
||||
fill: '#8F959E',
|
||||
}),
|
||||
],
|
||||
),
|
||||
])
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,17 +62,17 @@ export default {
|
|||
{
|
||||
viewBox: '0 0 20 20',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg'
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M3.60734 16.4448L3.34807 16.1624C2.44036 15.1737 1.75935 13.9944 1.36011 12.7066L1.24756 12.3435L2.95427 10.0001L1.24756 7.65668L1.36011 7.29361C1.75935 6.00574 2.44036 4.82649 3.34807 3.83779L3.60734 3.55539L6.47552 3.86889L7.64049 1.21319L8.01405 1.12909C8.66134 0.983366 9.32633 0.90918 10.0004 0.90918C10.6744 0.90918 11.3394 0.983366 11.9867 1.12909L12.3603 1.21319L13.5252 3.86889L16.3934 3.55539L16.6527 3.83779C17.5604 4.82649 18.2414 6.00574 18.6406 7.29361L18.7532 7.65668L17.0465 10.0001L18.7532 12.3435L18.6406 12.7066C18.2414 13.9944 17.5604 15.1737 16.6527 16.1624L16.3934 16.4448L13.5252 16.1313L12.3603 18.787L11.9867 18.8711C11.3394 19.0168 10.6744 19.091 10.0004 19.091C9.32633 19.091 8.66134 19.0168 8.01405 18.8711L7.64049 18.787L6.47552 16.1313L3.60734 16.4448ZM6.51159 14.6031C7.05002 14.5443 7.56436 14.8417 7.78194 15.3377L8.71565 17.4662C9.13677 17.5389 9.56603 17.5758 10.0004 17.5758C10.4347 17.5758 10.864 17.5389 11.2851 17.4662L12.2188 15.3377C12.4364 14.8417 12.9507 14.5443 13.4892 14.6031L15.7844 14.854C16.3387 14.1868 16.7757 13.4286 17.0741 12.6116L15.7038 10.7301C15.3869 10.295 15.3869 9.70511 15.7038 9.26999L17.0741 7.38847C16.7757 6.57146 16.3387 5.81331 15.7844 5.14609L13.4892 5.39696C12.9507 5.45581 12.4364 5.1584 12.2188 4.66238L11.2851 2.53389C10.864 2.46117 10.4347 2.42429 10.0004 2.42429C9.56603 2.42429 9.13677 2.46117 8.71565 2.53389L7.78194 4.66238C7.56436 5.1584 7.05002 5.45581 6.51159 5.39696L4.21641 5.14609C3.66208 5.81331 3.22502 6.57146 2.92666 7.38847L4.29697 9.26999C4.61387 9.70511 4.61387 10.295 4.29697 10.7301L2.92666 12.6116C3.22502 13.4286 3.66208 14.1868 4.21641 14.854L6.51159 14.6031ZM10.0004 13.788C7.91555 13.788 6.22693 12.0913 6.22693 10.0001C6.22693 7.9089 7.91555 6.2122 10.0004 6.2122C12.0852 6.2122 13.7738 7.9089 13.7738 10.0001C13.7738 12.0913 12.0852 13.788 10.0004 13.788ZM10.0004 12.2729C11.2468 12.2729 12.2587 11.2561 12.2587 10.0001C12.2587 8.74413 11.2468 7.72741 10.0004 7.72741C8.75397 7.72741 7.74208 8.74413 7.74208 10.0001C7.74208 11.2561 8.75397 12.2729 10.0004 12.2729Z',
|
||||
fill: 'currentColor'
|
||||
})
|
||||
]
|
||||
)
|
||||
fill: 'currentColor',
|
||||
}),
|
||||
],
|
||||
),
|
||||
])
|
||||
}
|
||||
},
|
||||
},
|
||||
'app-setting-active': {
|
||||
iconReader: () => {
|
||||
|
|
@ -82,16 +82,16 @@ export default {
|
|||
{
|
||||
viewBox: '0 0 20 20',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg'
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M3.26425 16.2151C2.35478 15.2292 1.65887 14.0432 1.25 12.7305L2.70785 10.7384C3.02952 10.2988 3.02952 9.70154 2.70785 9.26197L1.25 7.26979C1.65887 5.95714 2.35478 4.77112 3.26425 3.78522L5.71416 4.05172C6.25589 4.11065 6.77338 3.81185 6.99316 3.31321L7.98848 1.05505C8.63579 0.910018 9.30896 0.833496 10 0.833496C10.691 0.833496 11.3642 0.910018 12.0115 1.05505L13.0068 3.31321C13.2266 3.81185 13.7441 4.11065 14.2858 4.05172L16.7357 3.78522C17.6452 4.77112 18.3411 5.95714 18.75 7.26979L17.2921 9.26197C16.9705 9.70154 16.9705 10.2988 17.2921 10.7384L18.75 12.7305C18.3411 14.0432 17.6452 15.2292 16.7357 16.2151L14.2858 15.9486C13.7441 15.8897 13.2266 16.1885 13.0068 16.6871L12.0115 18.9453C11.3642 19.0903 10.691 19.1668 10 19.1668C9.30896 19.1668 8.63579 19.0903 7.98848 18.9453L6.99316 16.6871C6.77338 16.1885 6.25589 15.8897 5.71416 15.9486L3.26425 16.2151ZM10 13.3335C11.8409 13.3335 13.3333 11.8411 13.3333 10.0002C13.3333 8.15921 11.8409 6.66683 10 6.66683C8.15905 6.66683 6.66667 8.15921 6.66667 10.0002C6.66667 11.8411 8.15905 13.3335 10 13.3335Z',
|
||||
fill: 'currentColor'
|
||||
})
|
||||
]
|
||||
)
|
||||
fill: 'currentColor',
|
||||
}),
|
||||
],
|
||||
),
|
||||
])
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,3 +166,4 @@ export const iconMap: any = {
|
|||
// 动态加载的图标
|
||||
...dynamicIcons,
|
||||
}
|
||||
console.log(iconMap);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
</div>
|
||||
</slot>
|
||||
<slot name="subTitle"> </slot>
|
||||
<div class="shared" v-if="isShared">{{t('views.system.shared')}}</div>
|
||||
</div>
|
||||
<div class="status-tag">
|
||||
<slot name="tag"> </slot>
|
||||
|
|
@ -60,8 +61,9 @@ const props = withDefaults(
|
|||
* 是否展示icon
|
||||
*/
|
||||
showIcon?: boolean
|
||||
isShared?: boolean
|
||||
}>(),
|
||||
{ title: t('common.title'), description: '', showIcon: true, border: true },
|
||||
{ title: t('common.title'), description: '', showIcon: true, border: true, isShared: false },
|
||||
)
|
||||
|
||||
const show = ref(false)
|
||||
|
|
@ -102,6 +104,28 @@ function subHoveredEnter() {
|
|||
}
|
||||
}
|
||||
|
||||
.shared {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 17px;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #646a73;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background-color: #1f23291a;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 32px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import {Codemirror} from 'vue-codemirror'
|
|||
import {python} from '@codemirror/lang-python'
|
||||
import {oneDark} from '@codemirror/theme-one-dark'
|
||||
import {linter, type Diagnostic} from '@codemirror/lint'
|
||||
import ToolApi from '@/api/tool/tool'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
|
||||
defineOptions({name: 'CodemirrorEditor'})
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,18 @@
|
|||
clearable
|
||||
class="p-8"
|
||||
/>
|
||||
<div
|
||||
@click="handleSharedNodeClick"
|
||||
v-if="isShared"
|
||||
class="shared-knowledge"
|
||||
:class="currentNodeKey === 'share' && 'active'"
|
||||
>
|
||||
<AppIcon
|
||||
:iconName="currentNodeKey === 'share' ? 'app-folder-share-active' : 'app-folder-share'"
|
||||
style="font-size: 20px"
|
||||
></AppIcon>
|
||||
<span class="name">{{ $t('views.system.share_knowledge') }}</span>
|
||||
</div>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="data"
|
||||
|
|
@ -21,7 +33,7 @@
|
|||
<template #default="{ node, data }">
|
||||
<div class="custom-tree-node flex align-center">
|
||||
<AppIcon iconName="app-folder" style="font-size: 16px"></AppIcon>
|
||||
<span class="ml-8" >{{ node.label }}</span>
|
||||
<span class="ml-8">{{ node.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
|
|
@ -31,6 +43,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import type { TreeInstance } from 'element-plus'
|
||||
import { t } from '@/locales'
|
||||
defineOptions({ name: 'FolderTree' })
|
||||
const props = defineProps({
|
||||
data: {
|
||||
|
|
@ -41,10 +54,15 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: 'root',
|
||||
},
|
||||
isShared: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
interface Tree {
|
||||
name: string
|
||||
children?: Tree[]
|
||||
id?: string
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
|
|
@ -69,4 +87,48 @@ const filterNode = (value: string, data: Tree) => {
|
|||
const handleNodeClick = (data: Tree) => {
|
||||
emit('handleNodeClick', data)
|
||||
}
|
||||
|
||||
const handleSharedNodeClick = () => {
|
||||
treeRef.value?.setCurrentKey(null)
|
||||
emit('handleNodeClick', { id: 'share', name: t('views.system.share_knowledge') })
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.shared-knowledge {
|
||||
padding-left: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--app-text-color-light-1);
|
||||
color: var(--el-menu-text-color);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
background-color: #1f232926;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<template>
|
||||
<div class="app-layout">
|
||||
<div class="app-header">
|
||||
<UserHeader />
|
||||
<SystemHeader v-if="isShared"></SystemHeader>
|
||||
<UserHeader v-else />
|
||||
</div>
|
||||
<div class="app-main">
|
||||
<layout-container>
|
||||
|
|
@ -14,10 +15,18 @@
|
|||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import UserHeader from '@/layout/layout-header/UserHeader.vue'
|
||||
import SystemHeader from '@/layout/layout-header/SystemHeader.vue'
|
||||
import Sidebar from '@/layout/components/sidebar/index.vue'
|
||||
import AppMain from '@/layout/app-main/index.vue'
|
||||
import useStore from '@/stores'
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
|
||||
const isShared = computed(() => {
|
||||
return route.path.endsWith('hared')
|
||||
})
|
||||
const { user } = useStore()
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
export default {
|
||||
title: '系统管理',
|
||||
subTitle: '系统设置',
|
||||
shared: '共享',
|
||||
shared_resources: '共享资源',
|
||||
share_knowledge: '共享知识库',
|
||||
authorized_workspace:'授权工作空间',
|
||||
test: '测试连接',
|
||||
testSuccess: '测试连接成功',
|
||||
testFailed: '测试连接失败',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
const ModelRouter = {
|
||||
path: '/knowledge/system',
|
||||
name: 'knowledgeSharedSystem',
|
||||
meta: { title: 'views.knowledge.title', permission: 'KNOWLEDGE:READ' },
|
||||
hidden: true,
|
||||
redirect: '/knowledge',
|
||||
component: () => import('@/layout/layout-template/SimpleLayout.vue'),
|
||||
children: [
|
||||
|
||||
{
|
||||
path: '/knowledge/system/document/upload/shared',
|
||||
name: 'UploadDocumentSharedSystem',
|
||||
meta: { activeMenu: '/knowledge1/system' },
|
||||
component: () => import('@/views/document-shared-system/UploadDocument.vue'),
|
||||
hidden: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default ModelRouter
|
||||
|
|
@ -19,7 +19,7 @@ const ModelRouter = {
|
|||
meta: { activeMenu: '/knowledge' },
|
||||
component: () => import('@/views/document/UploadDocument.vue'),
|
||||
hidden: true,
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
const DocumentRouter = {
|
||||
path: '/knowledge/system/:id/',
|
||||
name: 'KnowledgeDetailSharedSystem',
|
||||
meta: { title: 'common.fileUpload.document', activeMenu: '/knowledge', breadcrumb: true },
|
||||
component: () => import('@/layout/layout-template/MainLayout.vue'),
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: 'documentShared',
|
||||
name: 'DocumentSharedSystem',
|
||||
meta: {
|
||||
icon: 'app-document',
|
||||
iconActive: 'app-document-active',
|
||||
title: 'common.fileUpload.document',
|
||||
active: 'documentShared',
|
||||
parentPath: '/knowledge/system/:id/',
|
||||
parentName: 'KnowledgeDetailSharedSystem',
|
||||
},
|
||||
component: () => import('@/views/document-shared-system/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'problemShared',
|
||||
name: 'ProblemSharedSystem',
|
||||
meta: {
|
||||
icon: 'app-problems',
|
||||
iconActive: 'QuestionFilled',
|
||||
title: 'views.problem.title',
|
||||
active: 'problemShared',
|
||||
parentPath: '/knowledge/system/:id/',
|
||||
parentName: 'KnowledgeDetailSharedSystem',
|
||||
},
|
||||
component: () => import('@/views/problem-shared-system/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'hit-test-shared',
|
||||
name: 'DatasetHitTestSharedSystem',
|
||||
meta: {
|
||||
icon: 'app-hit-test',
|
||||
title: 'views.application.hitTest.title',
|
||||
active: 'hit-test-shared',
|
||||
parentPath: '/knowledge/system/:id/',
|
||||
parentName: 'KnowledgeDetailSharedSystem',
|
||||
},
|
||||
component: () => import('@/views/hit-test-shared-system/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'settingShared',
|
||||
name: 'settingSharedSystem',
|
||||
meta: {
|
||||
icon: 'app-setting',
|
||||
iconActive: 'app-setting-active',
|
||||
title: 'common.setting',
|
||||
active: 'settingShared',
|
||||
parentPath: '/knowledge/system/:id/',
|
||||
parentName: 'KnowledgeDetailSharedSystem',
|
||||
},
|
||||
component: () => import('@/views/knowledge-shared-system/KnowledgeSetting.vue'),
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
export default DocumentRouter
|
||||
|
|
@ -27,9 +27,9 @@ const DocumentRouter = {
|
|||
title: 'views.problem.title',
|
||||
active: 'problem',
|
||||
parentPath: '/knowledge/:id/:folderId',
|
||||
parentName: 'KnowledgeDetail'
|
||||
parentName: 'KnowledgeDetail',
|
||||
},
|
||||
component: () => import('@/views/problem/index.vue')
|
||||
component: () => import('@/views/problem/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'hit-test',
|
||||
|
|
@ -39,9 +39,9 @@ const DocumentRouter = {
|
|||
title: 'views.application.hitTest.title',
|
||||
active: 'hit-test',
|
||||
parentPath: '/knowledge/:id/:folderId',
|
||||
parentName: 'KnowledgeDetail'
|
||||
parentName: 'KnowledgeDetail',
|
||||
},
|
||||
component: () => import('@/views/hit-test/index.vue')
|
||||
component: () => import('@/views/hit-test/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'setting',
|
||||
|
|
@ -52,10 +52,10 @@ const DocumentRouter = {
|
|||
title: 'common.setting',
|
||||
active: 'setting',
|
||||
parentPath: '/knowledge/:id/:folderId',
|
||||
parentName: 'KnowledgeDetail'
|
||||
parentName: 'KnowledgeDetail',
|
||||
},
|
||||
component: () => import('@/views/knowledge/KnowledgeSetting.vue')
|
||||
}
|
||||
component: () => import('@/views/knowledge/KnowledgeSetting.vue'),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
const ParagraphRouter = {
|
||||
path: '/paragraph/system/:id/:documentId/shared',
|
||||
name: 'ParagraphSharedSystem',
|
||||
meta: { title: 'common.fileUpload.document', activeMenu: '/knowledge', breadcrumb: true },
|
||||
component: () => import('@/layout/layout-template/SimpleLayout.vue'),
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: '/paragraph/system/:id/:documentId/shared',
|
||||
name: 'ParagraphIndexSharedSystem',
|
||||
meta: { activeMenu: '/knowledge' },
|
||||
component: () => import('@/views/paragraph-shared-system/index.vue'),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default ParagraphRouter
|
||||
|
|
@ -45,6 +45,53 @@ const systemRouter = {
|
|||
},
|
||||
component: () => import('@/views/role/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system/shared',
|
||||
name: 'shared',
|
||||
meta: {
|
||||
icon: 'app-folder-share',
|
||||
iconActive: 'app-folder-share-active',
|
||||
title: 'views.system.shared_resources',
|
||||
activeMenu: '/system',
|
||||
parentPath: '/system',
|
||||
parentName: 'system',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/system/shared/knowledge',
|
||||
name: 'knowledgeBase',
|
||||
meta: {
|
||||
title: 'views.knowledge.title',
|
||||
activeMenu: '/system',
|
||||
parentPath: '/system',
|
||||
parentName: 'system',
|
||||
},
|
||||
component: () => import('@/views/knowledge-shared-system/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system/shared/tool',
|
||||
name: 'tools',
|
||||
meta: {
|
||||
title: 'views.tool.title',
|
||||
activeMenu: '/system',
|
||||
parentPath: '/system',
|
||||
parentName: 'system',
|
||||
},
|
||||
component: () => import('@/views/tool-shared-system/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system/shared/model',
|
||||
name: 'models',
|
||||
meta: {
|
||||
title: 'views.model.title',
|
||||
activeMenu: '/system',
|
||||
parentPath: '/system',
|
||||
parentName: 'system',
|
||||
},
|
||||
component: () => import('@/views/model-shared-system/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/system/setting',
|
||||
name: 'setting',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { DeviceType, ValidType } from '@/enums/common'
|
||||
// import type { Ref } from 'vue'
|
||||
// import userApi from '@/api/user/user'
|
||||
|
||||
export interface commonTypes {
|
||||
breadcrumb: any
|
||||
paginationConfig: any | null
|
||||
search: any
|
||||
device: string
|
||||
}
|
||||
|
||||
const useCommonStore = defineStore('commo',{
|
||||
state: (): commonTypes => ({
|
||||
breadcrumb: null,
|
||||
// 搜索和分页缓存
|
||||
paginationConfig: {},
|
||||
search: {},
|
||||
device: DeviceType.Desktop
|
||||
}),
|
||||
actions: {
|
||||
saveBreadcrumb(data: any) {
|
||||
this.breadcrumb = data
|
||||
},
|
||||
savePage(val: string, data: any) {
|
||||
this.paginationConfig[val] = data
|
||||
},
|
||||
saveCondition(val: string, data: any) {
|
||||
this.search[val] = data
|
||||
},
|
||||
toggleDevice(value: DeviceType) {
|
||||
this.device = value
|
||||
},
|
||||
isMobile() {
|
||||
return this.device === DeviceType.Mobile
|
||||
},
|
||||
// async asyncGetValid(valid_type: ValidType, valid_count: number, loading?: Ref<boolean>) {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// userApi
|
||||
// .getValid(valid_type, valid_count, loading)
|
||||
// .then((data) => {
|
||||
// resolve(data)
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// reject(error)
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
}
|
||||
})
|
||||
|
||||
export default useCommonStore
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import documentApi from '@/api/shared/document'
|
||||
import { type Ref } from 'vue'
|
||||
|
||||
const useDocumentStore = defineStore('documen', {
|
||||
state: () => ({}),
|
||||
actions: {
|
||||
async asyncGetAllDocument(id: string, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
documentApi
|
||||
.getAllDocument(id, loading)
|
||||
.then((res) => {
|
||||
resolve(res)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncPostDocument(knowledgeId: string, data: any, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
documentApi
|
||||
.postMulDocument(knowledgeId, data, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default useDocumentStore
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import {defineStore} from 'pinia'
|
||||
import {type Ref} from 'vue'
|
||||
import folderApi from '@/api/folder'
|
||||
|
||||
const useFolderStore = defineStore('folde', {
|
||||
state: () => ({}),
|
||||
actions: {
|
||||
async asyncGetFolder(source: string, data: any, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
folderApi
|
||||
.getFolder(source, data, loading)
|
||||
.then((res) => {
|
||||
resolve(res)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default useFolderStore
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import useCommonStore from './common'
|
||||
import useLoginStore from './login'
|
||||
import useUserStore from './user'
|
||||
import useFolderStore from './folder'
|
||||
import useThemeStore from './theme'
|
||||
import useKnowledgeStore from './knowledge'
|
||||
import useModelStore from './model'
|
||||
import usePromptStore from './prompt'
|
||||
import useProblemStore from './problem'
|
||||
import useParagraphStore from './paragraph'
|
||||
import useDocumentStore from './document'
|
||||
|
||||
const useStore = () => ({
|
||||
common: useCommonStore(),
|
||||
login: useLoginStore(),
|
||||
user: useUserStore(),
|
||||
folder: useFolderStore(),
|
||||
theme: useThemeStore(),
|
||||
knowledge: useKnowledgeStore(),
|
||||
model: useModelStore(),
|
||||
prompt: usePromptStore(),
|
||||
problem: useProblemStore(),
|
||||
paragraph: useParagraphStore(),
|
||||
document: useDocumentStore(),
|
||||
})
|
||||
|
||||
export default useStore
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import {defineStore} from 'pinia'
|
||||
import type {knowledgeData} from '@/api/type/knowledge'
|
||||
import type {UploadUserFile} from 'element-plus'
|
||||
import knowledgeApi from '@/api/shared/knowledge'
|
||||
import {type Ref} from 'vue'
|
||||
|
||||
export interface knowledgeStateTypes {
|
||||
baseInfo: knowledgeData | null
|
||||
webInfo: any
|
||||
documentsType: string
|
||||
documentsFiles: UploadUserFile[]
|
||||
}
|
||||
|
||||
const useKnowledgeStore = defineStore('knowledg', {
|
||||
state: (): knowledgeStateTypes => ({
|
||||
baseInfo: null,
|
||||
webInfo: null,
|
||||
documentsType: '',
|
||||
documentsFiles: [],
|
||||
}),
|
||||
actions: {
|
||||
saveBaseInfo(info: knowledgeData | null) {
|
||||
this.baseInfo = info
|
||||
},
|
||||
saveWebInfo(info: any) {
|
||||
this.webInfo = info
|
||||
},
|
||||
saveDocumentsType(val: string) {
|
||||
this.documentsType = val
|
||||
},
|
||||
saveDocumentsFile(file: UploadUserFile[]) {
|
||||
this.documentsFiles = file
|
||||
},
|
||||
async asyncGetAllKnowledge(loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
knowledgeApi
|
||||
.getAllKnowledge(loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncGetKnowledgeDetail(
|
||||
knowledge_id: string,
|
||||
loading?: Ref<boolean>,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
knowledgeApi
|
||||
.getKnowledgeDetail(knowledge_id, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncSyncKnowledge(
|
||||
id: string,
|
||||
sync_type: string,
|
||||
loading?: Ref<boolean>,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
knowledgeApi
|
||||
.putSyncWebKnowledge(id, sync_type, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default useKnowledgeStore
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { type Ref } from 'vue'
|
||||
import loginApi from '@/api/user/login'
|
||||
import type { LoginRequest } from '@/api/type/login'
|
||||
import useUserStore from './user'
|
||||
|
||||
const useLoginStore = defineStore('logi', {
|
||||
state: () => ({
|
||||
token: '',
|
||||
userAccessToken: '',
|
||||
}),
|
||||
actions: {
|
||||
getToken(): string | null {
|
||||
if (this.token) {
|
||||
return this.token
|
||||
}
|
||||
const user = useUserStore()
|
||||
return user.userType === 1 ? localStorage.getItem('token') : this.getAccessToken()
|
||||
},
|
||||
getAccessToken() {
|
||||
const token = sessionStorage.getItem(`${this.userAccessToken}-accessToken`)
|
||||
if (token) {
|
||||
return token
|
||||
}
|
||||
const local_token = localStorage.getItem(`${token}-accessToken`)
|
||||
if (local_token) {
|
||||
return local_token
|
||||
}
|
||||
return localStorage.getItem(`accessToken`)
|
||||
},
|
||||
|
||||
async asyncLogin(data: LoginRequest, loading?: Ref<boolean>) {
|
||||
return loginApi.login(data).then((ok) => {
|
||||
this.token = ok?.data?.token
|
||||
localStorage.setItem('token', ok?.data?.token)
|
||||
const user = useUserStore()
|
||||
return user.profile(loading)
|
||||
})
|
||||
},
|
||||
async asyncLdapLogin(data: LoginRequest, loading?: Ref<boolean>) {
|
||||
return loginApi.ldapLogin(data).then((ok) => {
|
||||
this.token = ok?.data?.token
|
||||
localStorage.setItem('token', ok?.data?.token)
|
||||
const user = useUserStore()
|
||||
return user.profile(loading)
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default useLoginStore
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import {defineStore} from 'pinia'
|
||||
import {type Ref} from 'vue'
|
||||
import ModelApi from '@/api/shared/model'
|
||||
import ProviderApi from '@/api/shared/provider'
|
||||
import type {ListModelRequest} from '@/api/type/model'
|
||||
|
||||
const useModelStore = defineStore('mode', {
|
||||
state: () => ({}),
|
||||
actions: {
|
||||
async asyncGetModel(data?: ListModelRequest, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ModelApi.getModel(data, loading)
|
||||
.then((res) => {
|
||||
resolve(res)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncGetProvider(loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ProviderApi.getProvider(loading)
|
||||
.then((res) => {
|
||||
resolve(res)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default useModelStore
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import paragraphApi from '@/api/shared/paragraph'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
const useParagraphStore = defineStore('paragrap', {
|
||||
state: () => ({}),
|
||||
actions: {
|
||||
async asyncPutParagraph(
|
||||
knowledgeId: string,
|
||||
documentId: string,
|
||||
paragraphId: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
paragraphApi
|
||||
.putParagraph(knowledgeId, documentId, paragraphId, data, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
async asyncDelParagraph(
|
||||
knowledgeId: string,
|
||||
documentId: string,
|
||||
paragraphId: string,
|
||||
loading?: Ref<boolean>,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
paragraphApi
|
||||
.delParagraph(knowledgeId, documentId, paragraphId, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncDisassociationProblem(
|
||||
knowledgeId: string,
|
||||
documentId: string,
|
||||
paragraphId: string,
|
||||
problemId: string,
|
||||
loading?: Ref<boolean>,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const obj = {
|
||||
paragraphId,
|
||||
problemId,
|
||||
}
|
||||
paragraphApi
|
||||
.putDisassociationProblem(knowledgeId, documentId, obj, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncAssociationProblem(
|
||||
knowledgeId: string,
|
||||
documentId: string,
|
||||
paragraphId: string,
|
||||
problemId: string,
|
||||
loading?: Ref<boolean>,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const obj = {
|
||||
paragraphId,
|
||||
problemId,
|
||||
}
|
||||
paragraphApi
|
||||
.putAssociationProblem(knowledgeId, documentId, obj, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default useParagraphStore
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { type Ref } from 'vue'
|
||||
import problemApi from '@/api/shared/problem'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
|
||||
const useProblemStore = defineStore('proble', {
|
||||
state: () => ({}),
|
||||
actions: {
|
||||
async asyncPostProblem(knowledgeId: string, data: any, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
problemApi
|
||||
.postProblems(knowledgeId, data, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async asyncGetProblem(
|
||||
knowledgeId: string,
|
||||
page: pageRequest,
|
||||
param: any,
|
||||
loading?: Ref<boolean>,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
problemApi
|
||||
.getProblems(knowledgeId, page, param, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default useProblemStore
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { t } from '@/locales'
|
||||
export interface promptTypes {
|
||||
user: string
|
||||
formValue: { model_id: string; prompt: string }
|
||||
}
|
||||
|
||||
const usePromptStore = defineStore('promp', {
|
||||
state: (): promptTypes[] => JSON.parse(localStorage.getItem('PROMPT_CACHE') || '[]'),
|
||||
actions: {
|
||||
save(user: string, formValue: any) {
|
||||
this.$state.forEach((item: any, index: number) => {
|
||||
if (item.user === user) {
|
||||
this.$state.splice(index, 1)
|
||||
}
|
||||
})
|
||||
this.$state.push({ user, formValue })
|
||||
localStorage.setItem('PROMPT_CACHE', JSON.stringify(this.$state))
|
||||
},
|
||||
get(user: string) {
|
||||
for (let i = 0; i < this.$state.length; i++) {
|
||||
if (this.$state[i].user === user) {
|
||||
return this.$state[i].formValue
|
||||
}
|
||||
}
|
||||
return {
|
||||
model_id: '',
|
||||
prompt:
|
||||
t('views.document.generateQuestion.prompt1', { data: '{data}' }) +
|
||||
'<question></question>' +
|
||||
t('views.document.generateQuestion.prompt2'),
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default usePromptStore
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { useElementPlusTheme } from 'use-element-plus-theme'
|
||||
import ThemeApi from '@/api/system-settings/theme'
|
||||
import type {Ref} from "vue";
|
||||
export interface themeStateTypes {
|
||||
themeInfo: any
|
||||
}
|
||||
const defalueColor = '#3370FF'
|
||||
|
||||
const useThemeStore = defineStore('them', {
|
||||
state: (): themeStateTypes => ({
|
||||
themeInfo: null,
|
||||
}),
|
||||
actions: {
|
||||
isDefaultTheme() {
|
||||
return !this.themeInfo?.theme || this.themeInfo?.theme === defalueColor
|
||||
},
|
||||
|
||||
setTheme(data?: any) {
|
||||
const { changeTheme } = useElementPlusTheme(this.themeInfo?.theme || defalueColor)
|
||||
changeTheme(defalueColor)
|
||||
changeTheme(data?.['theme'])
|
||||
this.themeInfo = cloneDeep(data)
|
||||
},
|
||||
|
||||
// async theme(loading?: Ref<boolean>) {
|
||||
// return await ThemeApi.getThemeInfo(loading).then((ok) => {
|
||||
// this.setTheme(ok.data)
|
||||
// // window.document.title = this.themeInfo['title'] || 'MaxKB'
|
||||
// // const link = document.querySelector('link[rel="icon"]') as any
|
||||
// // if (link) {
|
||||
// // link['href'] = this.themeInfo['icon'] || '/favicon.ico'
|
||||
// // }
|
||||
// })
|
||||
// },
|
||||
},
|
||||
})
|
||||
|
||||
export default useThemeStore
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
import {defineStore} from 'pinia'
|
||||
import {type Ref} from 'vue'
|
||||
import type {User} from '@/api/type/user'
|
||||
import UserApi from '@/api/user/user'
|
||||
import LoginApi from '@/api/user/login'
|
||||
import {cloneDeep} from 'lodash'
|
||||
import ThemeApi from '@/api/system-settings/theme'
|
||||
// import { defaultPlatformSetting } from '@/utils/theme'
|
||||
import {useLocalStorage} from '@vueuse/core'
|
||||
import {localeConfigKey, getBrowserLang} from '@/locales/index'
|
||||
import useThemeStore from './theme'
|
||||
import {useElementPlusTheme} from "use-element-plus-theme";
|
||||
import {defaultPlatformSetting} from "@/utils/theme";
|
||||
|
||||
export interface userStateTypes {
|
||||
userType: number // 1 系统操作者 2 对话用户
|
||||
userInfo: User | null
|
||||
version?: string
|
||||
XPACK_LICENSE_IS_VALID: true
|
||||
isXPack: true
|
||||
themeInfo: any
|
||||
token: any
|
||||
}
|
||||
|
||||
const useLoginStore = defineStore('use', {
|
||||
state: (): userStateTypes => ({
|
||||
userType: 1, // 1 系统操作者 2 对话用户
|
||||
userInfo: null,
|
||||
version: '',
|
||||
XPACK_LICENSE_IS_VALID: false,
|
||||
isXPack: false,
|
||||
themeInfo: null,
|
||||
token: ''
|
||||
}),
|
||||
actions: {
|
||||
getLanguage() {
|
||||
return this.userType === 1
|
||||
? localStorage.getItem('MaxKB-locale') || getBrowserLang()
|
||||
: sessionStorage.getItem('language') || getBrowserLang()
|
||||
},
|
||||
isDefaultTheme() {
|
||||
return !this.themeInfo?.theme || this.themeInfo?.theme === '#3370FF'
|
||||
},
|
||||
setTheme(data: any) {
|
||||
const {changeTheme} = useElementPlusTheme(this.themeInfo?.theme)
|
||||
changeTheme(data?.['theme'])
|
||||
this.themeInfo = cloneDeep(data)
|
||||
},
|
||||
async profile(loading?: Ref<boolean>) {
|
||||
return UserApi.getUserProfile(loading).then((ok) => {
|
||||
this.userInfo = ok.data
|
||||
useLocalStorage<string>(localeConfigKey, 'en-US').value =
|
||||
ok?.data?.language || this.getLanguage()
|
||||
const theme = useThemeStore()
|
||||
theme.setTheme()
|
||||
return this.asyncGetProfile()
|
||||
})
|
||||
},
|
||||
async asyncGetProfile() {
|
||||
return new Promise((resolve, reject) => {
|
||||
UserApi.getProfile()
|
||||
.then(async (ok) => {
|
||||
// this.version = ok.data?.version || '-'
|
||||
this.isXPack = true
|
||||
this.XPACK_LICENSE_IS_VALID = true
|
||||
|
||||
if (this.isEnterprise()) {
|
||||
// await this.theme()
|
||||
} else {
|
||||
this.themeInfo = {
|
||||
...defaultPlatformSetting
|
||||
}
|
||||
}
|
||||
resolve(ok)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
getPermissions() {
|
||||
if (this.userInfo) {
|
||||
return this.isXPack && this.XPACK_LICENSE_IS_VALID
|
||||
? [...this.userInfo?.permissions, 'x-pack']
|
||||
: this.userInfo?.permissions
|
||||
} else {
|
||||
return this.userInfo?.permissions
|
||||
}
|
||||
},
|
||||
getRole() {
|
||||
if (this.userInfo) {
|
||||
return this.userInfo?.role
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
async theme(loading?: Ref<boolean>) {
|
||||
return await ThemeApi.getThemeInfo(loading).then((ok) => {
|
||||
this.setTheme(ok.data)
|
||||
// window.document.title = this.themeInfo['title'] || 'MaxKB'
|
||||
// const link = document.querySelector('link[rel="icon"]') as any
|
||||
// if (link) {
|
||||
// link['href'] = this.themeInfo['icon'] || '/favicon.ico'
|
||||
// }
|
||||
})
|
||||
},
|
||||
showXpack() {
|
||||
return this.isXPack
|
||||
},
|
||||
|
||||
isExpire() {
|
||||
return this.isXPack && !this.XPACK_LICENSE_IS_VALID
|
||||
},
|
||||
isEnterprise() {
|
||||
return this.isXPack && this.XPACK_LICENSE_IS_VALID
|
||||
},
|
||||
|
||||
// changeUserType(num: number, token?: string) {
|
||||
// this.userType = num
|
||||
// this.userAccessToken = token
|
||||
// },
|
||||
|
||||
|
||||
async dingCallback(code: string) {
|
||||
return LoginApi.getDingCallback(code).then((ok) => {
|
||||
this.token = ok.data
|
||||
localStorage.setItem('token', ok.data)
|
||||
return this.profile()
|
||||
})
|
||||
},
|
||||
async dingOauth2Callback(code: string) {
|
||||
return LoginApi.getDingOauth2Callback(code).then((ok) => {
|
||||
this.token = ok.data
|
||||
localStorage.setItem('token', ok.data)
|
||||
return this.profile()
|
||||
})
|
||||
},
|
||||
async wecomCallback(code: string) {
|
||||
return LoginApi.getWecomCallback(code).then((ok) => {
|
||||
this.token = ok.data
|
||||
localStorage.setItem('token', ok.data)
|
||||
return this.profile()
|
||||
})
|
||||
},
|
||||
async larkCallback(code: string) {
|
||||
return LoginApi.getLarkCallback(code).then((ok) => {
|
||||
this.token = ok.data
|
||||
localStorage.setItem('token', ok.data)
|
||||
return this.profile()
|
||||
})
|
||||
},
|
||||
|
||||
async logout() {
|
||||
return LoginApi.logout().then(() => {
|
||||
localStorage.removeItem('token')
|
||||
return true
|
||||
})
|
||||
},
|
||||
async getAuthType() {
|
||||
return LoginApi.getAuthType().then((ok) => {
|
||||
return ok.data
|
||||
})
|
||||
},
|
||||
async getQrType() {
|
||||
return LoginApi.getQrType().then((ok) => {
|
||||
return ok.data
|
||||
})
|
||||
},
|
||||
async getQrSource() {
|
||||
return LoginApi.getQrSource().then((ok) => {
|
||||
return ok.data
|
||||
})
|
||||
},
|
||||
async postUserLanguage(lang: string, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
LoginApi.postLanguage({ language: lang }, loading)
|
||||
.then(async (ok) => {
|
||||
useLocalStorage(localeConfigKey, 'en-US').value = lang
|
||||
window.location.reload()
|
||||
resolve(ok)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default useLoginStore
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
<template>
|
||||
<div class="upload-document p-12-24">
|
||||
<div class="flex align-center mb-16">
|
||||
<back-button to="-1" style="margin-left: -4px"></back-button>
|
||||
<h3 style="display: inline-block">{{ $t('views.document.uploadDocument') }}</h3>
|
||||
</div>
|
||||
<el-card style="--el-card-padding: 0">
|
||||
<div class="upload-document__main flex" v-loading="loading">
|
||||
<div class="upload-document__component main-calc-height">
|
||||
<el-scrollbar>
|
||||
<template v-if="active === 0">
|
||||
<div class="upload-component p-24">
|
||||
<!-- 上传文档 -->
|
||||
<UploadComponent ref="UploadComponentRef" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="active === 1">
|
||||
<SetRules ref="SetRulesRef" />
|
||||
</template>
|
||||
<template v-else-if="active === 2">
|
||||
<ResultSuccess :data="successInfo" />
|
||||
</template>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<div class="upload-document__footer text-right border-t" v-if="active !== 2">
|
||||
<el-button @click="router.go(-1)" :disabled="SetRulesRef?.loading || loading">{{
|
||||
$t('common.cancel')
|
||||
}}</el-button>
|
||||
<el-button @click="prev" v-if="active === 1" :disabled="SetRulesRef?.loading || loading">{{
|
||||
$t('views.document.buttons.prev')
|
||||
}}</el-button>
|
||||
<el-button
|
||||
@click="next"
|
||||
type="primary"
|
||||
v-if="active === 0"
|
||||
:disabled="SetRulesRef?.loading || loading"
|
||||
>
|
||||
{{
|
||||
documentsType === 'txt'
|
||||
? $t('views.document.buttons.next')
|
||||
: $t('views.document.buttons.import')
|
||||
}}
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="submit"
|
||||
type="primary"
|
||||
v-if="active === 1"
|
||||
:disabled="SetRulesRef?.loading || loading"
|
||||
>
|
||||
{{ $t('views.document.buttons.import') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onUnmounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import SetRules from './upload/SetRules.vue'
|
||||
import ResultSuccess from './upload/ResultSuccess.vue'
|
||||
import UploadComponent from './upload/UploadComponent.vue'
|
||||
import documentApi from '@/api/shared/document'
|
||||
import { MsgConfirm, MsgSuccess } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
const { knowledge, document } = useStore()
|
||||
const documentsFiles = computed(() => knowledge.documentsFiles)
|
||||
const documentsType = computed(() => knowledge.documentsType)
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const {
|
||||
query: { id }, // id为knowledgeID,有id的是上传文档
|
||||
} = route
|
||||
|
||||
const SetRulesRef = ref()
|
||||
const UploadComponentRef = ref()
|
||||
|
||||
const loading = ref(false)
|
||||
const disabled = ref(false)
|
||||
const active = ref(0)
|
||||
const successInfo = ref<any>(null)
|
||||
async function next() {
|
||||
disabled.value = true
|
||||
if (await UploadComponentRef.value.validate()) {
|
||||
if (documentsType.value === 'QA') {
|
||||
const fd = new FormData()
|
||||
documentsFiles.value.forEach((item: any) => {
|
||||
if (item?.raw) {
|
||||
fd.append('file', item?.raw)
|
||||
}
|
||||
})
|
||||
if (id) {
|
||||
// QA文档上传
|
||||
documentApi.postQADocument(id as string, fd, loading).then((res) => {
|
||||
MsgSuccess(t('common.submitSuccess'))
|
||||
clearStore()
|
||||
router.push({ path: `/knowledge/${id}/document` })
|
||||
})
|
||||
}
|
||||
} else if (documentsType.value === 'table') {
|
||||
const fd = new FormData()
|
||||
documentsFiles.value.forEach((item: any) => {
|
||||
if (item?.raw) {
|
||||
fd.append('file', item?.raw)
|
||||
}
|
||||
})
|
||||
if (id) {
|
||||
// table文档上传
|
||||
documentApi.postTableDocument(id as string, fd, loading).then((res) => {
|
||||
MsgSuccess(t('common.submitSuccess'))
|
||||
clearStore()
|
||||
router.push({ path: `/knowledge/${id}/document` })
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (active.value++ > 2) active.value = 0
|
||||
}
|
||||
} else {
|
||||
disabled.value = false
|
||||
}
|
||||
}
|
||||
const prev = () => {
|
||||
active.value = 0
|
||||
}
|
||||
|
||||
function clearStore() {
|
||||
knowledge.saveDocumentsFile([])
|
||||
knowledge.saveDocumentsType('')
|
||||
}
|
||||
function submit() {
|
||||
loading.value = true
|
||||
const documents = [] as any
|
||||
SetRulesRef.value?.paragraphList.map((item: any) => {
|
||||
if (!SetRulesRef.value?.checkedConnect) {
|
||||
item.content.map((v: any) => {
|
||||
delete v['problem_list']
|
||||
})
|
||||
}
|
||||
documents.push({
|
||||
name: item.name,
|
||||
paragraphs: item.content,
|
||||
source_file_id: item.source_file_id,
|
||||
})
|
||||
})
|
||||
|
||||
if (id) {
|
||||
// 上传文档
|
||||
document
|
||||
.asyncPostDocument(id as string, documents)
|
||||
.then(() => {
|
||||
MsgSuccess(t('common.submitSuccess'))
|
||||
clearStore()
|
||||
router.push({ path: `/knowledge/system/${id}/documentShared` })
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
function back() {
|
||||
if (documentsFiles.value?.length > 0) {
|
||||
MsgConfirm(t('common.tip'), t('views.document.tip.saveMessage'), {
|
||||
confirmButtonText: t('common.confirm'),
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
router.go(-1)
|
||||
clearStore()
|
||||
})
|
||||
.catch(() => {})
|
||||
} else {
|
||||
router.go(-1)
|
||||
}
|
||||
}
|
||||
onUnmounted(() => {
|
||||
clearStore()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.upload-document {
|
||||
&__steps {
|
||||
min-width: 450px;
|
||||
max-width: 800px;
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
padding-right: 60px;
|
||||
|
||||
:deep(.el-step__line) {
|
||||
left: 64% !important;
|
||||
right: -33% !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__component {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
&__footer {
|
||||
padding: 16px 24px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #ffffff;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.upload-component {
|
||||
width: 70%;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="$t('components.selectParagraph.title')"
|
||||
:before-close="close"
|
||||
width="450"
|
||||
>
|
||||
<el-radio-group v-model="state" class="radio-block">
|
||||
<el-radio value="error" size="large">{{
|
||||
$t('components.selectParagraph.error')
|
||||
}}</el-radio>
|
||||
<el-radio value="all" size="large">{{ $t('components.selectParagraph.all') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="close">{{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit"> {{ $t('common.submit') }} </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const state = ref<'all' | 'error'>('error')
|
||||
const stateMap = {
|
||||
all: ['0', '1', '2', '3', '4', '5', 'n'],
|
||||
error: ['0', '1', '3', '4', '5', 'n']
|
||||
}
|
||||
const submit_handle = ref<(stateList: Array<string>) => void>()
|
||||
const submit = () => {
|
||||
if (submit_handle.value) {
|
||||
submit_handle.value(stateMap[state.value])
|
||||
}
|
||||
close()
|
||||
}
|
||||
|
||||
const open = (handle: (stateList: Array<string>) => void) => {
|
||||
submit_handle.value = handle
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const close = () => {
|
||||
submit_handle.value = undefined
|
||||
dialogVisible.value = false
|
||||
}
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
width="550"
|
||||
>
|
||||
<el-form
|
||||
label-position="top"
|
||||
ref="webFormRef"
|
||||
:rules="rules"
|
||||
:model="form"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
<el-form-item
|
||||
:label="$t('views.document.form.source_url.label')"
|
||||
prop="source_url"
|
||||
v-if="isImport"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.source_url"
|
||||
:placeholder="$t('views.document.form.source_url.placeholder')"
|
||||
:rows="10"
|
||||
type="textarea"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-else-if="!isImport && documentType === '1'"
|
||||
:label="$t('views.document.form.source_url.label')"
|
||||
prop="source_url"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.source_url"
|
||||
:placeholder="$t('views.document.form.source_url.requiredMessage')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.document.form.selector.label')" v-if="documentType === '1'">
|
||||
<el-input
|
||||
v-model="form.selector"
|
||||
:placeholder="$t('views.document.form.selector.placeholder')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!isImport">
|
||||
<template #label>
|
||||
<div class="flex align-center">
|
||||
<span class="mr-4">{{ $t('views.document.form.hit_handling_method.label') }}</span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('views.document.form.hit_handling_method.tooltip')"
|
||||
placement="right"
|
||||
>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-radio-group v-model="form.hit_handling_method" class="radio-block mt-4">
|
||||
<template v-for="(value, key) of hitHandlingMethod" :key="key">
|
||||
<el-radio :value="key">{{ $t(value) }} </el-radio>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="directly_return_similarity"
|
||||
v-if="!isImport && form.hit_handling_method === 'directly_return'"
|
||||
>
|
||||
<div class="lighter w-full" style="margin-top: -20px">
|
||||
<span>{{ $t('views.document.form.similarity.label') }}</span>
|
||||
<el-input-number
|
||||
v-model="form.directly_return_similarity"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:precision="3"
|
||||
:step="0.1"
|
||||
:value-on-clear="0"
|
||||
controls-position="right"
|
||||
size="small"
|
||||
class="ml-4 mr-4"
|
||||
/><span>{{ $t('views.document.form.similarity.placeholder') }}</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit(webFormRef)" :loading="loading">
|
||||
{{ $t('common.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import documentApi from '@/api/shared/document'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
import { hitHandlingMethod } from '@/enums/document'
|
||||
import { t } from '@/locales'
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }
|
||||
} = route as any
|
||||
|
||||
const props = defineProps({
|
||||
title: String
|
||||
})
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
const webFormRef = ref()
|
||||
const loading = ref<boolean>(false)
|
||||
const isImport = ref<boolean>(false)
|
||||
const form = ref<any>({
|
||||
source_url: '',
|
||||
selector: '',
|
||||
hit_handling_method: 'optimization',
|
||||
directly_return_similarity: 0.9
|
||||
})
|
||||
|
||||
// 文档设置
|
||||
const documentId = ref('')
|
||||
const documentType = ref<string | number>('') //文档类型:1: web文档;0:普通文档
|
||||
|
||||
// 批量设置
|
||||
const documentList = ref<Array<string>>([])
|
||||
|
||||
const rules = reactive({
|
||||
source_url: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.document.form.source_url.requiredMessage'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
directly_return_similarity: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.document.form.similarity.requiredMessage'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
form.value = {
|
||||
source_url: '',
|
||||
selector: '',
|
||||
hit_handling_method: 'optimization',
|
||||
directly_return_similarity: 0.9
|
||||
}
|
||||
isImport.value = false
|
||||
documentType.value = ''
|
||||
documentId.value = ''
|
||||
documentList.value = []
|
||||
}
|
||||
})
|
||||
|
||||
const open = (row: any, list: Array<string>) => {
|
||||
if (row) {
|
||||
documentType.value = row.type
|
||||
documentId.value = row.id
|
||||
form.value = {
|
||||
hit_handling_method: row.hit_handling_method,
|
||||
directly_return_similarity: row.directly_return_similarity,
|
||||
...row.meta
|
||||
}
|
||||
isImport.value = false
|
||||
} else if (list) {
|
||||
// 批量设置
|
||||
documentList.value = list
|
||||
} else {
|
||||
// 导入 只有web文档类型
|
||||
documentType.value = '1'
|
||||
isImport.value = true
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
if (isImport.value) {
|
||||
const obj = {
|
||||
source_url_list: form.value.source_url.split('\n'),
|
||||
selector: form.value.selector
|
||||
}
|
||||
documentApi.postWebDocument(id, obj, loading).then(() => {
|
||||
MsgSuccess(t('views.document.tip.importMessage'))
|
||||
emit('refresh')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
} else {
|
||||
if (documentId.value) {
|
||||
const obj = {
|
||||
hit_handling_method: form.value.hit_handling_method,
|
||||
directly_return_similarity: form.value.directly_return_similarity,
|
||||
meta: {
|
||||
source_url: form.value.source_url,
|
||||
selector: form.value.selector
|
||||
}
|
||||
}
|
||||
documentApi.putDocument(id, documentId.value, obj, loading).then(() => {
|
||||
MsgSuccess(t('common.settingSuccess'))
|
||||
emit('refresh')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
} else if (documentList.value.length > 0) {
|
||||
// 批量设置
|
||||
const obj = {
|
||||
hit_handling_method: form.value.hit_handling_method,
|
||||
directly_return_similarity: form.value.directly_return_similarity,
|
||||
id_list: documentList.value
|
||||
}
|
||||
documentApi.putBatchEditHitHandling(id, obj, loading).then(() => {
|
||||
MsgSuccess(t('common.settingSuccess'))
|
||||
emit('refresh')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.log.selectDataset')"
|
||||
v-model="dialogVisible"
|
||||
width="600"
|
||||
class="select-dataset-dialog"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<template #header="{ titleId, titleClass }">
|
||||
<div class="my-header flex">
|
||||
<h4 :id="titleId" :class="titleClass">{{ $t('views.log.selectDataset') }}</h4>
|
||||
<el-button link class="ml-16" @click="refresh">
|
||||
<el-icon class="mr-4"><Refresh /></el-icon>{{ $t('common.refresh') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="content-height">
|
||||
<el-radio-group v-model="selectDataset" class="card__radio">
|
||||
<el-scrollbar height="500">
|
||||
<div class="p-16">
|
||||
<el-row :gutter="12" v-loading="loading">
|
||||
<el-col :span="12" v-for="(item, index) in datasetList" :key="index" class="mb-16">
|
||||
<el-card shadow="never" :class="item.id === selectDataset ? 'active' : ''">
|
||||
<el-radio :value="item.id" size="large">
|
||||
<div class="flex align-center">
|
||||
<el-avatar
|
||||
v-if="item?.type === '0'"
|
||||
class="mr-8 avatar-blue"
|
||||
shape="square"
|
||||
:size="32"
|
||||
>
|
||||
<img src="@/assets/knowledge/icon_document.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<el-avatar
|
||||
v-if="item?.type === '1'"
|
||||
class="mr-8 avatar-purple"
|
||||
shape="square"
|
||||
:size="32"
|
||||
>
|
||||
<img src="@/assets/knowledge/icon_web.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<el-avatar
|
||||
v-if="item?.type === '2'"
|
||||
class="mr-8 avatar-purple"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img src="@/assets/knowledge/logo_lark.svg" style="width: 100%" alt="" />
|
||||
</el-avatar>
|
||||
<span class="ellipsis" :title="item.name">
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</div>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submitHandle" :disabled="!selectDataset || loading">
|
||||
{{ $t('common.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import documentApi from '@/api/shared/document'
|
||||
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
const { knowledge } = useStore()
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id } // id为datasetID
|
||||
} = route as any
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const selectDataset = ref('')
|
||||
const datasetList = ref<any>([])
|
||||
const documentList = ref<any>([])
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
selectDataset.value = ''
|
||||
datasetList.value = []
|
||||
documentList.value = []
|
||||
}
|
||||
})
|
||||
|
||||
const open = (list: any) => {
|
||||
documentList.value = list
|
||||
getDataset()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const submitHandle = () => {
|
||||
documentApi
|
||||
.putMigrateMulDocument(id, selectDataset.value, documentList.value, loading)
|
||||
.then((res) => {
|
||||
emit('refresh')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function getDataset() {
|
||||
knowledge.asyncGetAllKnowledge(loading).then((res: any) => {
|
||||
datasetList.value = res.data?.filter((v: any) => v.id !== id)
|
||||
})
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
getDataset()
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.select-dataset-dialog {
|
||||
padding: 0;
|
||||
.el-dialog__header {
|
||||
padding: 24px 24px 0 24px;
|
||||
}
|
||||
.el-dialog__body {
|
||||
padding: 8px !important;
|
||||
}
|
||||
.el-dialog__footer {
|
||||
padding: 0 24px 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<el-popover
|
||||
v-model:visible="visible"
|
||||
placement="top"
|
||||
trigger="hover"
|
||||
:popper-style="{ width: 'auto' }"
|
||||
>
|
||||
<template #default
|
||||
><StatusTable
|
||||
v-if="visible"
|
||||
:status="status"
|
||||
:statusMeta="statusMeta"
|
||||
:taskTypeMap="taskTypeMap"
|
||||
:stateMap="stateMap"
|
||||
></StatusTable>
|
||||
</template>
|
||||
<template #reference>
|
||||
<el-text v-if="aggStatus?.value === State.SUCCESS || aggStatus?.value === State.REVOKED">
|
||||
<el-icon class="success"><SuccessFilled /></el-icon>
|
||||
{{ stateMap[aggStatus.value](aggStatus.key) }}
|
||||
</el-text>
|
||||
<el-text v-else-if="aggStatus?.value === State.FAILURE">
|
||||
<el-icon class="danger"><CircleCloseFilled /></el-icon>
|
||||
{{ stateMap[aggStatus.value](aggStatus.key) }}
|
||||
</el-text>
|
||||
<el-text v-else-if="aggStatus?.value === State.STARTED">
|
||||
<el-icon class="is-loading primary"><Loading /></el-icon>
|
||||
{{ stateMap[aggStatus.value](aggStatus.key) }}
|
||||
</el-text>
|
||||
<el-text v-else-if="aggStatus?.value === State.PENDING">
|
||||
<el-icon class="is-loading primary"><Loading /></el-icon>
|
||||
{{ stateMap[aggStatus.value](aggStatus.key) }}
|
||||
</el-text>
|
||||
<el-text v-else-if="aggStatus?.value === State.REVOKE">
|
||||
<el-icon class="is-loading primary"><Loading /></el-icon>
|
||||
{{ stateMap[aggStatus.value](aggStatus.key) }}
|
||||
</el-text>
|
||||
</template>
|
||||
</el-popover>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { TaskType, State } from '@/utils/status'
|
||||
import StatusTable from '@/views/document-shared-system/component/StatusTable.vue'
|
||||
import { t } from '@/locales'
|
||||
const props = defineProps<{ status: string; statusMeta: any }>()
|
||||
const visible = ref<boolean>(false)
|
||||
const checkList: Array<string> = [
|
||||
State.REVOKE,
|
||||
State.STARTED,
|
||||
State.PENDING,
|
||||
State.FAILURE,
|
||||
State.REVOKED,
|
||||
State.SUCCESS
|
||||
]
|
||||
const aggStatus = computed(() => {
|
||||
let obj = { key: 0, value: '' }
|
||||
for (const i in checkList) {
|
||||
const state = checkList[i]
|
||||
const index = props.status.indexOf(state)
|
||||
if (index > -1) {
|
||||
obj = { key: props.status.length - index, value: state }
|
||||
break
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
const startedMap = {
|
||||
[TaskType.EMBEDDING]: t('views.document.fileStatus.EMBEDDING'),
|
||||
[TaskType.GENERATE_PROBLEM]: t('views.document.fileStatus.GENERATE'),
|
||||
[TaskType.SYNC]: t('views.document.fileStatus.SYNC')
|
||||
}
|
||||
const taskTypeMap = {
|
||||
[TaskType.EMBEDDING]: t('views.knowledge.setting.vectorization'),
|
||||
[TaskType.GENERATE_PROBLEM]: t('views.document.generateQuestion.title'),
|
||||
[TaskType.SYNC]: t('views.knowledge.setting.sync')
|
||||
}
|
||||
const stateMap: any = {
|
||||
[State.PENDING]: (type: number) => t('views.document.fileStatus.PENDING'),
|
||||
[State.STARTED]: (type: number) => startedMap[type],
|
||||
[State.REVOKE]: (type: number) => t('views.document.fileStatus.REVOKE'),
|
||||
[State.REVOKED]: (type: number) => t('views.document.fileStatus.SUCCESS'),
|
||||
[State.FAILURE]: (type: number) => t('views.document.fileStatus.FAILURE'),
|
||||
[State.SUCCESS]: (type: number) => t('views.document.fileStatus.SUCCESS'),
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<template>
|
||||
<div v-for="status in statusTable" :key="status.type" >
|
||||
<span> {{ taskTypeMap[status.type] }}:</span>
|
||||
<span>
|
||||
<el-text v-if="status.state === State.SUCCESS || status.state === State.REVOKED">
|
||||
<el-icon class="success"><SuccessFilled /></el-icon>
|
||||
{{ stateMap[status.state](status.type) }}
|
||||
</el-text>
|
||||
<el-text v-else-if="status.state === State.FAILURE">
|
||||
<el-icon class="danger"><CircleCloseFilled /></el-icon>
|
||||
{{ stateMap[status.state](status.type) }}
|
||||
</el-text>
|
||||
<el-text v-else-if="status.state === State.STARTED">
|
||||
<el-icon class="is-loading primary"><Loading /></el-icon>
|
||||
{{ stateMap[status.state](status.type) }}
|
||||
</el-text>
|
||||
<el-text v-else-if="status.state === State.PENDING">
|
||||
<el-icon class="is-loading primary"><Loading /></el-icon>
|
||||
{{ stateMap[status.state](status.type) }}
|
||||
</el-text>
|
||||
<el-text v-else-if="status.state === State.REVOKE">
|
||||
<el-icon class="is-loading primary"><Loading /></el-icon>
|
||||
{{ stateMap[status.state](status.type) }}
|
||||
</el-text>
|
||||
</span>
|
||||
<span
|
||||
class="ml-8 lighter"
|
||||
:style="{ color: [State.FAILURE, State.REVOKED].includes(status.state) ? '#F54A45' : '' }"
|
||||
>
|
||||
{{ $t('views.document.fileStatus.finish') }}
|
||||
{{
|
||||
Object.keys(status.aggs ? status.aggs : {})
|
||||
.filter((k) => k == State.SUCCESS)
|
||||
.map((k) => status.aggs[k])
|
||||
.reduce((x: any, y: any) => x + y, 0)
|
||||
}}/{{
|
||||
Object.values(status.aggs ? status.aggs : {}).reduce((x: any, y: any) => x + y, 0)
|
||||
}}</span
|
||||
>
|
||||
<el-text type="info" class="ml-12">
|
||||
{{
|
||||
status.time
|
||||
? status.time[status.state == State.REVOKED ? State.REVOKED : State.PENDING]?.substring(
|
||||
0,
|
||||
19
|
||||
)
|
||||
: undefined
|
||||
}}
|
||||
</el-text>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Status, TaskType, State, type TaskTypeInterface } from '@/utils/status'
|
||||
import { mergeWith } from 'lodash'
|
||||
const props = defineProps<{ status: string; statusMeta: any; stateMap: any; taskTypeMap: any }>()
|
||||
|
||||
const parseAgg = (agg: { count: number; status: string }) => {
|
||||
const status = new Status(agg.status)
|
||||
return Object.keys(TaskType)
|
||||
.map((key) => {
|
||||
const value = TaskType[key as keyof TaskTypeInterface]
|
||||
return { [value]: { [status.task_status[value]]: agg.count } }
|
||||
})
|
||||
.reduce((x, y) => ({ ...x, ...y }), {})
|
||||
}
|
||||
|
||||
const customizer: (x: any, y: any) => any = (objValue: any, srcValue: any) => {
|
||||
if (objValue == undefined && srcValue) {
|
||||
return srcValue
|
||||
}
|
||||
if (srcValue == undefined && objValue) {
|
||||
return objValue
|
||||
}
|
||||
// 如果是数组,我们将元素进行聚合
|
||||
if (typeof objValue === 'object' && typeof srcValue === 'object') {
|
||||
// 若是object类型的对象,我们进行递归
|
||||
return mergeWith(objValue, srcValue, customizer)
|
||||
} else {
|
||||
// 否则,单纯的将值进行累加
|
||||
return objValue + srcValue
|
||||
}
|
||||
}
|
||||
const aggs = computed(() => {
|
||||
return (props.statusMeta.aggs ? props.statusMeta.aggs : [])
|
||||
.map((agg: any) => {
|
||||
return parseAgg(agg)
|
||||
})
|
||||
.reduce((x: any, y: any) => {
|
||||
return mergeWith(x, y, customizer)
|
||||
}, {})
|
||||
})
|
||||
|
||||
const statusTable = computed(() => {
|
||||
return Object.keys(TaskType)
|
||||
.map((key) => {
|
||||
const value = TaskType[key as keyof TaskTypeInterface]
|
||||
const parseStatus = new Status(props.status)
|
||||
return {
|
||||
type: value,
|
||||
state: parseStatus.task_status[value],
|
||||
aggs: aggs.value[value],
|
||||
time: props.statusMeta.state_time[value]
|
||||
}
|
||||
})
|
||||
.filter((item) => item.state !== State.IGNORED)
|
||||
})
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<el-scrollbar>
|
||||
<el-result icon="success" :title="`🎉 ${$t('views.knowledge.ResultSuccess.title')} 🎉`">
|
||||
<template #sub-title>
|
||||
<div class="mt-8">
|
||||
<span class="bold">{{ data?.document_list.length || 0 }}</span>
|
||||
<el-text type="info" class="ml-4">{{ $t('common.fileUpload.document') }}</el-text>
|
||||
<el-divider direction="vertical" />
|
||||
<span class="bold">{{ paragraph_count || 0 }}</span>
|
||||
<el-text type="info" class="ml-4">{{
|
||||
$t('views.knowledge.ResultSuccess.paragraph')
|
||||
}}</el-text>
|
||||
<el-divider direction="vertical" />
|
||||
<span class="bold">{{ numberFormat(char_length) || 0 }}</span>
|
||||
<el-text type="info" class="ml-4">{{ $t('common.character') }} </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #extra>
|
||||
<el-button @click="router.push({ path: `/knowledge` })">{{
|
||||
$t('views.knowledge.ResultSuccess.buttons.toDataset')
|
||||
}}</el-button>
|
||||
<el-button type="primary" @click="router.push({ path: `/knowledge/${data?.id}/${currentFolder.id}/document` })">{{
|
||||
$t('views.knowledge.ResultSuccess.buttons.toDocument')
|
||||
}}</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
<div class="result-success">
|
||||
<p class="bolder">{{ $t('views.knowledge.ResultSuccess.documentList') }}</p>
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="file-List-card mt-8"
|
||||
v-for="(item, index) in data?.document_list"
|
||||
:key="index"
|
||||
>
|
||||
<div class="flex-between">
|
||||
<div class="flex">
|
||||
<img :src="getImgUrl(item && item?.name)" alt="" width="40" />
|
||||
<div class="ml-8">
|
||||
<p>{{ item && item?.name }}</p>
|
||||
<el-text type="info" size="small">{{ filesize(item && item?.char_length) }}</el-text>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-text type="info" class="mr-16"
|
||||
>{{ item && item?.paragraph_count }}
|
||||
{{ $t('views.knowledge.ResultSuccess.paragraph_count') }}</el-text
|
||||
>
|
||||
<el-text v-if="item.status === '1'">
|
||||
<el-icon class="success"><SuccessFilled /></el-icon>
|
||||
</el-text>
|
||||
<el-text v-else-if="item.status === '2'">
|
||||
<el-icon class="danger"><CircleCloseFilled /></el-icon>
|
||||
</el-text>
|
||||
<el-text v-else-if="item.status === '0'">
|
||||
<el-icon class="is-loading primary"><Loading /></el-icon> {{ $t('views.knowledge.ResultSuccess.loading') }}...
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { numberFormat } from '@/utils/utils'
|
||||
import { filesize, getImgUrl } from '@/utils/utils'
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
const router = useRouter()
|
||||
const paragraph_count = computed(() =>
|
||||
props.data?.document_list.reduce((sum: number, obj: any) => (sum += obj.paragraph_count), 0)
|
||||
)
|
||||
|
||||
const char_length = computed(
|
||||
() =>
|
||||
props.data?.document_list.reduce((sum: number, obj: any) => (sum += obj.char_length), 0) || 0
|
||||
)
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.result-success {
|
||||
width: 70%;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
<template>
|
||||
<div class="set-rules">
|
||||
<el-row>
|
||||
<el-col :span="10" class="p-24">
|
||||
<h4 class="title-decoration-1 mb-16">{{ $t('views.document.setRules.title.setting') }}</h4>
|
||||
<div class="set-rules__right">
|
||||
<el-scrollbar>
|
||||
<div class="left-height" @click.stop>
|
||||
<el-radio-group v-model="radio" class="set-rules__radio">
|
||||
<el-card shadow="never" class="mb-16" :class="radio === '1' ? 'active' : ''">
|
||||
<el-radio value="1" size="large">
|
||||
<p class="mb-4">{{ $t('views.document.setRules.intelligent.label') }}</p>
|
||||
<el-text type="info">{{
|
||||
$t('views.document.setRules.intelligent.text')
|
||||
}}</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mb-16" :class="radio === '2' ? 'active' : ''">
|
||||
<el-radio value="2" size="large">
|
||||
<p class="mb-4">{{ $t('views.document.setRules.advanced.label') }}</p>
|
||||
<el-text type="info">
|
||||
{{ $t('views.document.setRules.advanced.text') }}
|
||||
</el-text>
|
||||
</el-radio>
|
||||
|
||||
<el-card
|
||||
v-if="radio === '2'"
|
||||
shadow="never"
|
||||
class="card-never mt-16"
|
||||
style="margin-left: 30px"
|
||||
>
|
||||
<div class="set-rules__form">
|
||||
<div class="form-item mb-16">
|
||||
<div class="title flex align-center mb-8">
|
||||
<span style="margin-right: 4px">{{
|
||||
$t('views.document.setRules.patterns.label')
|
||||
}}</span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('views.document.setRules.patterns.tooltip')"
|
||||
placement="right"
|
||||
>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div @click.stop>
|
||||
<el-select
|
||||
v-model="form.patterns"
|
||||
multiple
|
||||
allow-create
|
||||
default-first-option
|
||||
filterable
|
||||
:placeholder="$t('views.document.setRules.patterns.placeholder')"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in splitPatternList"
|
||||
:key="index"
|
||||
:label="item.key"
|
||||
:value="item.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item mb-16">
|
||||
<div class="title mb-8">
|
||||
{{ $t('views.document.setRules.limit.label') }}
|
||||
</div>
|
||||
<el-slider
|
||||
v-model="form.limit"
|
||||
show-input
|
||||
:show-input-controls="false"
|
||||
:min="50"
|
||||
:max="100000"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-item mb-16">
|
||||
<div class="title mb-8">
|
||||
{{ $t('views.document.setRules.with_filter.label') }}
|
||||
</div>
|
||||
<el-switch size="small" v-model="form.with_filter" />
|
||||
<div style="margin-top: 4px">
|
||||
<el-text type="info">
|
||||
{{ $t('views.document.setRules.with_filter.text') }}</el-text
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-card>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<div>
|
||||
<el-checkbox
|
||||
v-model="checkedConnect"
|
||||
@change="changeHandle"
|
||||
style="white-space: normal"
|
||||
>
|
||||
{{ $t('views.document.setRules.checkedConnect.label') }}
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<div class="text-right mt-8">
|
||||
<el-button @click="splitDocument">
|
||||
{{ $t('views.document.buttons.preview') }}</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="14" class="p-24 border-l">
|
||||
<div v-loading="loading">
|
||||
<h4 class="title-decoration-1 mb-8">{{ $t('views.document.setRules.title.preview') }}</h4>
|
||||
|
||||
<ParagraphPreview v-model:data="paragraphList" :isConnect="checkedConnect" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, reactive, watch } from 'vue'
|
||||
import ParagraphPreview from '@/views/knowledge/component/ParagraphPreview.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { cutFilename } from '@/utils/utils'
|
||||
import documentApi from '@/api/shared/document'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import type { KeyValue } from '@/api/type/common'
|
||||
const { knowledge } = useStore()
|
||||
const documentsFiles = computed(() => knowledge.documentsFiles)
|
||||
const splitPatternList = ref<Array<KeyValue<string, string>>>([])
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
query: { id }, // id为datasetID
|
||||
} = route as any
|
||||
const radio = ref('1')
|
||||
const loading = ref(false)
|
||||
const paragraphList = ref<any[]>([])
|
||||
const patternLoading = ref<boolean>(false)
|
||||
const checkedConnect = ref<boolean>(false)
|
||||
|
||||
const firstChecked = ref(true)
|
||||
|
||||
const form = reactive<{
|
||||
patterns: Array<string>
|
||||
limit: number
|
||||
with_filter: boolean
|
||||
[propName: string]: any
|
||||
}>({
|
||||
patterns: [],
|
||||
limit: 500,
|
||||
with_filter: true
|
||||
})
|
||||
|
||||
function changeHandle(val: boolean) {
|
||||
if (val && firstChecked.value) {
|
||||
paragraphList.value = paragraphList.value.map((item: any) => ({
|
||||
...item,
|
||||
content: item.content.map((v: any) => ({
|
||||
...v,
|
||||
problem_list: v.title.trim()
|
||||
? [
|
||||
{
|
||||
content: v.title.trim()
|
||||
}
|
||||
]
|
||||
: []
|
||||
}))
|
||||
}))
|
||||
firstChecked.value = false
|
||||
}
|
||||
}
|
||||
function splitDocument() {
|
||||
loading.value = true
|
||||
const fd = new FormData()
|
||||
documentsFiles.value.forEach((item) => {
|
||||
if (item?.raw) {
|
||||
fd.append('file', item?.raw)
|
||||
}
|
||||
})
|
||||
if (radio.value === '2') {
|
||||
Object.keys(form).forEach((key) => {
|
||||
if (key == 'patterns') {
|
||||
form.patterns.forEach((item) => fd.append('patterns', item))
|
||||
} else {
|
||||
fd.append(key, form[key])
|
||||
}
|
||||
})
|
||||
}
|
||||
documentApi
|
||||
.postSplitDocument(fd, id)
|
||||
.then((res: any) => {
|
||||
const list = res.data
|
||||
|
||||
list.map((item: any) => {
|
||||
if (item.name.length > 128) {
|
||||
item.name = cutFilename(item.name, 128)
|
||||
}
|
||||
if (checkedConnect.value) {
|
||||
item.content.map((v: any) => {
|
||||
v['problem_list'] = v.title.trim()
|
||||
? [
|
||||
{
|
||||
content: v.title.trim()
|
||||
}
|
||||
]
|
||||
: []
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
paragraphList.value = list
|
||||
loading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const initSplitPatternList = () => {
|
||||
documentApi.listSplitPattern(patternLoading).then((ok) => {
|
||||
splitPatternList.value = ok.data
|
||||
})
|
||||
}
|
||||
|
||||
watch(radio, () => {
|
||||
if (radio.value === '2') {
|
||||
initSplitPatternList()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
splitDocument()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
paragraphList,
|
||||
checkedConnect,
|
||||
loading
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.set-rules {
|
||||
width: 100%;
|
||||
|
||||
.left-height {
|
||||
max-height: calc(var(--create-dataset-height) - 110px);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
&__radio {
|
||||
width: 100%;
|
||||
display: block;
|
||||
|
||||
.el-radio {
|
||||
white-space: break-spaces;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
line-height: 22px;
|
||||
color: var(--app-text-color);
|
||||
}
|
||||
|
||||
:deep(.el-radio__label) {
|
||||
padding-left: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
:deep(.el-radio__input) {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
}
|
||||
.active {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&__form {
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
<template>
|
||||
<h4 class="title-decoration-1 mb-8">{{ $t('views.document.uploadDocument') }}</h4>
|
||||
<el-form
|
||||
ref="FormRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
<div class="mt-16 mb-16">
|
||||
<el-radio-group v-model="form.fileType" @change="radioChange" class="app-radio-button-group">
|
||||
<el-radio-button value="txt">{{ $t('views.document.fileType.txt.label') }}</el-radio-button>
|
||||
<el-radio-button value="table">{{
|
||||
$t('views.document.fileType.table.label')
|
||||
}}</el-radio-button>
|
||||
<el-radio-button value="QA">{{ $t('views.document.fileType.QA.label') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<el-form-item prop="fileList" v-if="form.fileType === 'QA'">
|
||||
<div class="update-info flex p-8-12 border-r-4 mb-16 w-full">
|
||||
<div class="mt-4">
|
||||
<AppIcon iconName="app-warning-colorful" style="font-size: 16px"></AppIcon>
|
||||
</div>
|
||||
<div class="ml-16 lighter">
|
||||
<p>
|
||||
{{ $t('views.document.fileType.QA.tip1') }}
|
||||
<el-button type="primary" link @click="downloadTemplate('excel')">
|
||||
{{ $t('views.document.upload.download') }} Excel
|
||||
{{ $t('views.document.upload.template') }}
|
||||
</el-button>
|
||||
<el-button type="primary" link @click="downloadTemplate('csv')">
|
||||
{{ $t('views.document.upload.download') }} CSV
|
||||
{{ $t('views.document.upload.template') }}
|
||||
</el-button>
|
||||
</p>
|
||||
<p>{{ $t('views.document.fileType.QA.tip2') }}</p>
|
||||
<p>{{ $t('views.document.fileType.QA.tip3') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<el-upload
|
||||
:webkitdirectory="false"
|
||||
class="w-full mb-4"
|
||||
drag
|
||||
multiple
|
||||
v-model:file-list="form.fileList"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept=".xlsx, .xls, .csv,.zip"
|
||||
:limit="50"
|
||||
:on-exceed="onExceed"
|
||||
:on-change="fileHandleChange"
|
||||
@click.prevent="handlePreview(false)"
|
||||
>
|
||||
<img src="@/assets/upload-icon.svg" alt="" />
|
||||
<div class="el-upload__text">
|
||||
<p>
|
||||
{{ $t('views.document.upload.uploadMessage') }}
|
||||
<em class="hover" @click.prevent="handlePreview(false)">
|
||||
{{ $t('views.document.upload.selectFile') }}
|
||||
</em>
|
||||
<em class="hove ml-4" @click.prevent="handlePreview(true)">
|
||||
{{ $t('views.document.upload.selectFiles') }}
|
||||
</em>
|
||||
</p>
|
||||
<div class="upload__decoration">
|
||||
<p>{{ $t('views.document.upload.formats') }}XLS、XLSX、CSV、ZIP</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item prop="fileList" v-else-if="form.fileType === 'table'">
|
||||
<div class="update-info flex p-8-12 border-r-4 mb-16 w-full">
|
||||
<div class="mt-4">
|
||||
<AppIcon iconName="app-warning-colorful" style="font-size: 16px"></AppIcon>
|
||||
</div>
|
||||
<div class="ml-16 lighter">
|
||||
<p>
|
||||
{{ $t('views.document.fileType.table.tip1') }}
|
||||
<el-button type="primary" link @click="downloadTableTemplate('excel')">
|
||||
{{ $t('views.document.upload.download') }} Excel
|
||||
{{ $t('views.document.upload.template') }}
|
||||
</el-button>
|
||||
<el-button type="primary" link @click="downloadTableTemplate('csv')">
|
||||
{{ $t('views.document.upload.download') }} CSV
|
||||
{{ $t('views.document.upload.template') }}
|
||||
</el-button>
|
||||
</p>
|
||||
<p>{{ $t('views.document.fileType.table.tip2') }}</p>
|
||||
<p>{{ $t('views.document.fileType.table.tip3') }}</p>
|
||||
<p>{{ $t('views.document.fileType.table.tip4') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<el-upload
|
||||
:webkitdirectory="false"
|
||||
class="w-full mb-4"
|
||||
drag
|
||||
multiple
|
||||
v-model:file-list="form.fileList"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept=".xlsx, .xls, .csv"
|
||||
:limit="50"
|
||||
:on-exceed="onExceed"
|
||||
:on-change="fileHandleChange"
|
||||
@click.prevent="handlePreview(false)"
|
||||
>
|
||||
<img src="@/assets/upload-icon.svg" alt="" />
|
||||
<div class="el-upload__text">
|
||||
<p>
|
||||
{{ $t('views.document.upload.uploadMessage') }}
|
||||
<em class="hover" @click.prevent="handlePreview(false)">
|
||||
{{ $t('views.document.upload.selectFile') }}
|
||||
</em>
|
||||
<em class="hover ml-4" @click.prevent="handlePreview(true)">
|
||||
{{ $t('views.document.upload.selectFiles') }}
|
||||
</em>
|
||||
</p>
|
||||
<div class="upload__decoration">
|
||||
<p>{{ $t('views.document.upload.formats') }}XLS、XLSX、CSV</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item prop="fileList" v-else>
|
||||
<div class="update-info flex p-8-12 border-r-4 mb-16 w-full">
|
||||
<div class="mt-4">
|
||||
<AppIcon iconName="app-warning-colorful" style="font-size: 16px"></AppIcon>
|
||||
</div>
|
||||
<div class="ml-16 lighter">
|
||||
<p>{{ $t('views.document.fileType.txt.tip1') }}</p>
|
||||
<p>{{ $t('views.document.fileType.txt.tip2') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<el-upload
|
||||
:webkitdirectory="false"
|
||||
class="w-full"
|
||||
drag
|
||||
multiple
|
||||
v-model:file-list="form.fileList"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept=".txt, .md, .log, .docx, .pdf, .html,.zip,.xlsx,.xls,.csv"
|
||||
:limit="50"
|
||||
:on-exceed="onExceed"
|
||||
:on-change="fileHandleChange"
|
||||
@click.prevent="handlePreview(false)"
|
||||
>
|
||||
<img src="@/assets/upload-icon.svg" alt="" />
|
||||
<div class="el-upload__text">
|
||||
<p>
|
||||
{{ $t('views.document.upload.uploadMessage') }}
|
||||
<em class="hover" @click.prevent="handlePreview(false)">
|
||||
{{ $t('views.document.upload.selectFile') }}
|
||||
</em>
|
||||
<em class="hover ml-4" @click.prevent="handlePreview(true)">
|
||||
{{ $t('views.document.upload.selectFiles') }}
|
||||
</em>
|
||||
</p>
|
||||
<div class="upload__decoration">
|
||||
<p>
|
||||
{{
|
||||
$t('views.document.upload.formats')
|
||||
}}TXT、Markdown、PDF、DOCX、HTML、XLS、XLSX、CSV、ZIP
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row :gutter="8" v-if="form.fileList?.length">
|
||||
<template v-for="(item, index) in form.fileList" :key="index">
|
||||
<el-col :span="12" class="mb-8">
|
||||
<el-card shadow="never" class="file-List-card">
|
||||
<div class="flex-between">
|
||||
<div class="flex">
|
||||
<img :src="getImgUrl(item && item?.name)" alt="" width="40" />
|
||||
<div class="ml-8">
|
||||
<p>{{ item && item?.name }}</p>
|
||||
<el-text type="info" size="small">{{
|
||||
filesize(item && item?.size) || '0K'
|
||||
}}</el-text>
|
||||
</div>
|
||||
</div>
|
||||
<el-button text @click="deleteFile(index)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onUnmounted, onMounted, computed, watch, nextTick } from 'vue'
|
||||
import type { UploadFiles } from 'element-plus'
|
||||
import { filesize, getImgUrl, isRightType } from '@/utils/utils'
|
||||
import { MsgError } from '@/utils/message'
|
||||
import documentApi from '@/api/shared/document'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import { t } from '@/locales'
|
||||
const { knowledge } = useStore()
|
||||
const documentsFiles = computed(() => knowledge.documentsFiles)
|
||||
const documentsType = computed(() => knowledge.documentsType)
|
||||
const form = ref({
|
||||
fileType: 'txt',
|
||||
fileList: [] as any
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
fileList: [
|
||||
{ required: true, message: t('views.document.upload.requiredMessage'), trigger: 'change' }
|
||||
]
|
||||
})
|
||||
const FormRef = ref()
|
||||
|
||||
watch(form.value, (value) => {
|
||||
knowledge.saveDocumentsType(value.fileType)
|
||||
knowledge.saveDocumentsFile(value.fileList)
|
||||
})
|
||||
|
||||
function downloadTemplate(type: string) {
|
||||
documentApi.exportQATemplate(
|
||||
`${type}${t('views.document.upload.template')}.${type == 'csv' ? type : 'xlsx'}`,
|
||||
type
|
||||
)
|
||||
}
|
||||
|
||||
function downloadTableTemplate(type: string) {
|
||||
documentApi.exportTableTemplate(
|
||||
`${type}${t('views.document.upload.template')}.${type == 'csv' ? type : 'xlsx'}`,
|
||||
type
|
||||
)
|
||||
}
|
||||
|
||||
function radioChange() {
|
||||
form.value.fileList = []
|
||||
}
|
||||
|
||||
function deleteFile(index: number) {
|
||||
form.value.fileList.splice(index, 1)
|
||||
}
|
||||
|
||||
// 上传on-change事件
|
||||
const fileHandleChange = (file: any, fileList: UploadFiles) => {
|
||||
//1、判断文件大小是否合法,文件限制不能大于100M
|
||||
const isLimit = file?.size / 1024 / 1024 < 100
|
||||
if (!isLimit) {
|
||||
MsgError(t('views.document.upload.errorMessage1'))
|
||||
fileList.splice(-1, 1) //移除当前超出大小的文件
|
||||
return false
|
||||
}
|
||||
|
||||
if (!isRightType(file?.name, form.value.fileType)) {
|
||||
if (file?.name !== '.DS_Store') {
|
||||
MsgError(t('views.document.upload.errorMessage2'))
|
||||
}
|
||||
fileList.splice(-1, 1)
|
||||
return false
|
||||
}
|
||||
if (file?.size === 0) {
|
||||
MsgError(t('views.document.upload.errorMessage3'))
|
||||
fileList.splice(-1, 1)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const onExceed = () => {
|
||||
MsgError(t('views.document.upload.errorMessage4'))
|
||||
}
|
||||
|
||||
const handlePreview = (bool: boolean) => {
|
||||
let inputDom: any = null
|
||||
nextTick(() => {
|
||||
if (document.querySelector('.el-upload__input') != null) {
|
||||
inputDom = document.querySelector('.el-upload__input')
|
||||
inputDom.webkitdirectory = bool
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
表单校验
|
||||
*/
|
||||
function validate() {
|
||||
if (!FormRef.value) return
|
||||
return FormRef.value.validate((valid: any) => {
|
||||
return valid
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (documentsType.value) {
|
||||
form.value.fileType = documentsType.value
|
||||
}
|
||||
if (documentsFiles.value) {
|
||||
form.value.fileList = documentsFiles.value
|
||||
}
|
||||
})
|
||||
onUnmounted(() => {
|
||||
form.value = {
|
||||
fileType: 'txt',
|
||||
fileList: []
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
form
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.upload__decoration {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
.el-upload__text {
|
||||
.hover:hover {
|
||||
color: var(--el-color-primary-light-5);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,434 @@
|
|||
<template>
|
||||
<div class="hit-test p-16-24">
|
||||
<h4>
|
||||
{{ $t('views.application.hitTest.title') }}
|
||||
<el-text type="info" class="ml-4"> {{ $t('views.application.hitTest.text') }}</el-text>
|
||||
</h4>
|
||||
<el-card style="--el-card-padding: 0" class="hit-test__main p-16 mt-16 mb-16" v-loading="loading">
|
||||
<div class="question-title" :style="{ visibility: questionTitle ? 'visible' : 'hidden' }">
|
||||
<div class="avatar">
|
||||
<el-avatar>
|
||||
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
|
||||
</el-avatar>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h4 class="text break-all">{{ questionTitle }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
<el-scrollbar>
|
||||
<div class="hit-test-height">
|
||||
<el-empty
|
||||
v-if="first"
|
||||
:image="emptyImg"
|
||||
:description="$t('views.application.hitTest.emptyMessage1')"
|
||||
style="padding-top: 160px"
|
||||
:image-size="125"
|
||||
/>
|
||||
<el-empty
|
||||
v-else-if="paragraphDetail.length == 0"
|
||||
:description="$t('views.application.hitTest.emptyMessage2')"
|
||||
style="padding-top: 160px"
|
||||
:image-size="125"
|
||||
/>
|
||||
<el-row v-else>
|
||||
<el-col
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="12"
|
||||
:lg="8"
|
||||
:xl="6"
|
||||
v-for="(item, index) in paragraphDetail"
|
||||
:key="index"
|
||||
class="p-8"
|
||||
>
|
||||
<CardBox
|
||||
shadow="hover"
|
||||
:title="item.title || '-'"
|
||||
:description="item.content"
|
||||
class="document-card layout-bg layout-bg cursor"
|
||||
:class="item.is_active ? '' : 'disabled'"
|
||||
:showIcon="false"
|
||||
@click="editParagraph(item)"
|
||||
>
|
||||
<template #icon>
|
||||
<el-avatar class="mr-12 avatar-light" :size="22"> {{ index + 1 + '' }}</el-avatar>
|
||||
</template>
|
||||
<div class="active-button primary">{{ item.similarity?.toFixed(3) }}</div>
|
||||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<el-text>
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
{{ item?.document_name }}
|
||||
</el-text>
|
||||
<div v-if="item.trample_num || item.star_num">
|
||||
<span v-if="item.star_num">
|
||||
<AppIcon iconName="app-like-color"></AppIcon>
|
||||
{{ item.star_num }}
|
||||
</span>
|
||||
<span v-if="item.trample_num" class="ml-4">
|
||||
<AppIcon iconName="app-oppose-color"></AppIcon>
|
||||
{{ item.trample_num }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CardBox>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-card>
|
||||
<ParagraphDialog ref="ParagraphDialogRef" :title="title" @refresh="refresh" />
|
||||
|
||||
<div class="hit-test__operate">
|
||||
<el-popover :visible="popoverVisible" placement="right-end" :width="500" trigger="click">
|
||||
<template #reference>
|
||||
<el-button icon="Setting" class="mb-8" @click="settingChange('open')">{{
|
||||
$t('common.paramSetting')
|
||||
}}</el-button>
|
||||
</template>
|
||||
<div class="mb-16">
|
||||
<div class="title mb-8">
|
||||
{{ $t('views.application.dialog.selectSearchMode') }}
|
||||
</div>
|
||||
<el-radio-group
|
||||
v-model="cloneForm.search_mode"
|
||||
class="card__radio"
|
||||
@change="changeHandle"
|
||||
>
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-16"
|
||||
:class="cloneForm.search_mode === 'embedding' ? 'active' : ''"
|
||||
>
|
||||
<el-radio value="embedding" size="large">
|
||||
<p class="mb-4">
|
||||
{{ $t('views.application.dialog.vectorSearch') }}
|
||||
</p>
|
||||
<el-text type="info">{{
|
||||
$t('views.application.dialog.vectorSearchTooltip')
|
||||
}}</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-16"
|
||||
:class="cloneForm.search_mode === 'keywords' ? 'active' : ''"
|
||||
>
|
||||
<el-radio value="keywords" size="large">
|
||||
<p class="mb-4">
|
||||
{{ $t('views.application.dialog.fullTextSearch') }}
|
||||
</p>
|
||||
<el-text type="info">{{
|
||||
$t('views.application.dialog.fullTextSearchTooltip')
|
||||
}}</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-16"
|
||||
:class="cloneForm.search_mode === 'blend' ? 'active' : ''"
|
||||
>
|
||||
<el-radio value="blend" size="large">
|
||||
<p class="mb-4">
|
||||
{{ $t('views.application.dialog.hybridSearch') }}
|
||||
</p>
|
||||
<el-text type="info">{{
|
||||
$t('views.application.dialog.hybridSearchTooltip')
|
||||
}}</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="mb-16">
|
||||
<div class="title mb-8">
|
||||
{{ $t('views.application.dialog.similarityThreshold') }}
|
||||
</div>
|
||||
<el-input-number
|
||||
v-model="cloneForm.similarity"
|
||||
:min="0"
|
||||
:max="cloneForm.search_mode === 'blend' ? 2 : 1"
|
||||
:precision="3"
|
||||
:step="0.1"
|
||||
:value-on-clear="0"
|
||||
controls-position="right"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="mb-16">
|
||||
<div class="title mb-8">
|
||||
{{ $t('views.application.dialog.topReferences') }}
|
||||
</div>
|
||||
<el-input-number
|
||||
v-model="cloneForm.top_number"
|
||||
:min="1"
|
||||
:max="10000"
|
||||
controls-position="right"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="text-right">
|
||||
<el-button @click="popoverVisible = false">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="settingChange('close')">{{
|
||||
$t('common.confirm')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="operate-textarea flex">
|
||||
<el-input
|
||||
ref="quickInputRef"
|
||||
v-model="inputValue"
|
||||
type="textarea"
|
||||
:placeholder="$t('common.inputPlaceholder')"
|
||||
:autosize="{ minRows: 1, maxRows: 8 }"
|
||||
@keydown.enter="sendChatHandle($event)"
|
||||
/>
|
||||
<div class="operate">
|
||||
<el-button
|
||||
text
|
||||
class="sent-button"
|
||||
:disabled="isDisabledChart || loading"
|
||||
@click="sendChatHandle"
|
||||
>
|
||||
<img v-show="isDisabledChart || loading" src="@/assets/icon_send.svg" alt="" />
|
||||
<img
|
||||
v-show="!isDisabledChart && !loading"
|
||||
src="@/assets/icon_send_colorful.svg"
|
||||
alt=""
|
||||
/>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { nextTick, ref, onMounted, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import KnowledgeApi from '@/api/shared/knowledge'
|
||||
// import applicationApi from '@/api/application/application'
|
||||
import ParagraphDialog from '@/views/paragraph/component/ParagraphDialog.vue'
|
||||
import { arraySort } from '@/utils/common'
|
||||
import emptyImg from '@/assets/hit-test-empty.png'
|
||||
import { t } from '@/locales'
|
||||
const route = useRoute()
|
||||
const {
|
||||
meta: { activeMenu },
|
||||
params: { id },
|
||||
} = route as any
|
||||
|
||||
const quickInputRef = ref()
|
||||
const ParagraphDialogRef = ref()
|
||||
const loading = ref(false)
|
||||
const paragraphDetail = ref<any[]>([])
|
||||
const title = ref('')
|
||||
const inputValue = ref('')
|
||||
const formInline = ref({
|
||||
similarity: 0.6,
|
||||
top_number: 5,
|
||||
search_mode: 'embedding',
|
||||
})
|
||||
|
||||
// 第一次加载
|
||||
const first = ref(true)
|
||||
|
||||
const cloneForm = ref<any>({})
|
||||
|
||||
const popoverVisible = ref(false)
|
||||
const questionTitle = ref('')
|
||||
|
||||
const isDisabledChart = computed(() => !inputValue.value)
|
||||
|
||||
const isApplication = computed(() => {
|
||||
return activeMenu.includes('application')
|
||||
})
|
||||
const isDataset = computed(() => {
|
||||
return activeMenu.includes('knowledge')
|
||||
})
|
||||
|
||||
function changeHandle(val: string) {
|
||||
if (val === 'keywords') {
|
||||
cloneForm.value.similarity = 0
|
||||
} else {
|
||||
cloneForm.value.similarity = 0.6
|
||||
}
|
||||
}
|
||||
|
||||
function settingChange(val: string) {
|
||||
if (val === 'open') {
|
||||
popoverVisible.value = true
|
||||
cloneForm.value = cloneDeep(formInline.value)
|
||||
} else if (val === 'close') {
|
||||
popoverVisible.value = false
|
||||
formInline.value = cloneDeep(cloneForm.value)
|
||||
}
|
||||
}
|
||||
|
||||
function editParagraph(row: any) {
|
||||
title.value = t('views.paragraph.paragraphDetail')
|
||||
ParagraphDialogRef.value.open(row)
|
||||
}
|
||||
|
||||
function sendChatHandle(event: any) {
|
||||
if (!event?.ctrlKey && !event?.shiftKey && !event?.altKey && !event?.metaKey) {
|
||||
// 如果没有按下组合键,则会阻止默认事件
|
||||
event.preventDefault()
|
||||
if (!isDisabledChart.value && !loading.value) {
|
||||
getHitTestList()
|
||||
}
|
||||
} else {
|
||||
// 如果同时按下ctrl/shift/cmd/opt +enter,则会换行
|
||||
insertNewlineAtCursor(event)
|
||||
}
|
||||
}
|
||||
const insertNewlineAtCursor = (event?: any) => {
|
||||
const textarea = quickInputRef.value.$el.querySelector(
|
||||
'.el-textarea__inner',
|
||||
) as HTMLTextAreaElement
|
||||
const startPos = textarea.selectionStart
|
||||
const endPos = textarea.selectionEnd
|
||||
// 阻止默认行为(避免额外的换行符)
|
||||
event.preventDefault()
|
||||
// 在光标处插入换行符
|
||||
inputValue.value = inputValue.value.slice(0, startPos) + '\n' + inputValue.value.slice(endPos)
|
||||
nextTick(() => {
|
||||
textarea.setSelectionRange(startPos + 1, startPos + 1) // 光标定位到换行后位置
|
||||
})
|
||||
}
|
||||
|
||||
function getHitTestList() {
|
||||
const obj = {
|
||||
query_text: inputValue.value,
|
||||
...formInline.value,
|
||||
}
|
||||
if (isDataset.value) {
|
||||
knowledgeApi.getDatasetHitTest(id, obj, loading).then((res) => {
|
||||
paragraphDetail.value = res.data && arraySort(res.data, 'comprehensive_score', true)
|
||||
questionTitle.value = inputValue.value
|
||||
inputValue.value = ''
|
||||
first.value = false
|
||||
})
|
||||
} else if (isApplication.value) {
|
||||
applicationApi.getApplicationHitTest(id, obj, loading).then((res) => {
|
||||
paragraphDetail.value = res.data && arraySort(res.data, 'comprehensive_score', true)
|
||||
questionTitle.value = inputValue.value
|
||||
inputValue.value = ''
|
||||
first.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function refresh(data: any) {
|
||||
if (data) {
|
||||
const obj = paragraphDetail.value.filter((v) => v.id === data.id)[0]
|
||||
obj.content = data.content
|
||||
obj.title = data.title
|
||||
} else {
|
||||
paragraphDetail.value = []
|
||||
getHitTestList()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.hit-test {
|
||||
.question-title {
|
||||
.avatar {
|
||||
float: left;
|
||||
}
|
||||
.content {
|
||||
padding-left: 40px;
|
||||
.text {
|
||||
padding: 6px 0;
|
||||
height: 34px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__operate {
|
||||
.operate-textarea {
|
||||
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ffffff;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:has(.el-textarea__inner:focus) {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
}
|
||||
|
||||
:deep(.el-textarea__inner) {
|
||||
border-radius: 8px !important;
|
||||
box-shadow: none;
|
||||
resize: none;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
.operate {
|
||||
padding: 6px 10px;
|
||||
.sent-button {
|
||||
max-height: none;
|
||||
.el-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
:deep(.el-loading-spinner) {
|
||||
margin-top: -15px;
|
||||
.circular {
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.hit-test {
|
||||
&__header {
|
||||
position: absolute;
|
||||
right: calc(var(--app-base-px) * 3);
|
||||
}
|
||||
|
||||
.hit-test-height {
|
||||
height: calc(100vh - 300px);
|
||||
}
|
||||
.document-card {
|
||||
height: 210px;
|
||||
border: 1px solid var(--app-layout-bg-color);
|
||||
&:hover {
|
||||
background: #ffffff;
|
||||
border: 1px solid var(--el-border-color);
|
||||
}
|
||||
&.disabled {
|
||||
background: var(--app-layout-bg-color);
|
||||
border: 1px solid var(--app-layout-bg-color);
|
||||
:deep(.description) {
|
||||
color: var(--app-border-color-dark);
|
||||
}
|
||||
:deep(.title) {
|
||||
color: var(--app-border-color-dark);
|
||||
}
|
||||
}
|
||||
:deep(.description) {
|
||||
-webkit-line-clamp: 5 !important;
|
||||
height: 110px;
|
||||
}
|
||||
.active-button {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
modal-class="authorized-workspace"
|
||||
v-model="centerDialogVisible"
|
||||
title="授权工作空间"
|
||||
width="840"
|
||||
>
|
||||
<div class="tip">被授权的工作空间,可使用当前资源</div>
|
||||
<div class="form-type">类型</div>
|
||||
<el-radio-group v-model="listType" @change="handleListTypeChange">
|
||||
<el-radio value="WHITE_LIST">白名单</el-radio>
|
||||
<el-radio value="BLACK_LIST">黑名单</el-radio>
|
||||
</el-radio-group>
|
||||
<div class="form-type_work">选择工作空间</div>
|
||||
<div class="workspace-list" v-loading="loading">
|
||||
<div class="to-be_selected">
|
||||
<el-input
|
||||
v-model="search"
|
||||
:validate-event="false"
|
||||
:placeholder="$t('dynamicsForm.searchBar.placeholder')"
|
||||
style="width: 364px"
|
||||
clearable
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Search></Search>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-checkbox
|
||||
v-model="checkAll"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
v-if="!search"
|
||||
>
|
||||
全选
|
||||
</el-checkbox>
|
||||
<el-checkbox-group v-model="checkedWorkspace" @change="handleCheckedWorkspaceChange">
|
||||
<el-checkbox
|
||||
v-for="space in workspaceWithKeywords"
|
||||
:key="space"
|
||||
:label="space"
|
||||
:value="space"
|
||||
>
|
||||
<el-icon size="20">
|
||||
<momentsCategories></momentsCategories>
|
||||
</el-icon>
|
||||
{{ space }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<div class="selected">
|
||||
<div class="count">
|
||||
已选: {{ checkedWorkspace.length }} 个
|
||||
<el-button @click="clearWorkspaceAll" link type="primary"> 清空 </el-button>
|
||||
</div>
|
||||
<div class="list-item_primary" v-for="ele in checkedWorkspace">
|
||||
<el-icon size="20">
|
||||
<momentsCategories></momentsCategories>
|
||||
</el-icon>
|
||||
<div class="label">{{ ele }}</div>
|
||||
<el-icon @click="clearWorkspace(ele)" class="close" size="16">
|
||||
<closeIcon></closeIcon>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="centerDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm"> 添加 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import type { CheckboxValueType } from 'element-plus'
|
||||
import iconMap from '@/components/app-icon/icons/common'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import KnowledgeApi from '@/api/shared/knowledge'
|
||||
|
||||
const checkAll = ref(false)
|
||||
const isIndeterminate = ref(true)
|
||||
const checkedWorkspace = ref([])
|
||||
const workspace = ref([])
|
||||
const listType = ref('WHITE_LIST')
|
||||
const search = ref('')
|
||||
let knowledge_id = ''
|
||||
let currentType = 'Knowledge'
|
||||
const loading = ref(false)
|
||||
const centerDialogVisible = ref(false)
|
||||
let auth_list = []
|
||||
let un_auth_list = []
|
||||
const closeIcon = iconMap['close-outlined'].iconReader()
|
||||
const momentsCategories = iconMap['moments-categories'].iconReader()
|
||||
|
||||
const workspaceWithKeywords = computed(() => {
|
||||
return workspace.value.filter((ele) => ele.includes(search.value))
|
||||
})
|
||||
const handleCheckAllChange = (val: CheckboxValueType) => {
|
||||
checkedWorkspace.value = val ? workspace.value : []
|
||||
isIndeterminate.value = false
|
||||
if (val) {
|
||||
clearWorkspaceAll()
|
||||
} else {
|
||||
auth_list = [
|
||||
...workspace.value.map((ele) => ({ authentication_type: listType.value, workspace_id: ele })),
|
||||
...auth_list.filter((ele) => ele.authentication_type !== listType.value),
|
||||
]
|
||||
}
|
||||
}
|
||||
const handleCheckedWorkspaceChange = (value: CheckboxValueType[]) => {
|
||||
const checkedCount = value.length
|
||||
checkAll.value = checkedCount === workspace.value.length
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < workspace.value.length
|
||||
auth_list = [
|
||||
...value.map((ele) => ({ authentication_type: listType.value, workspace_id: ele })),
|
||||
...auth_list.filter((ele) => ele.authentication_type !== listType.value),
|
||||
]
|
||||
}
|
||||
|
||||
const open = ({ id }, type = 'Knowledge') => {
|
||||
knowledge_id = id
|
||||
auth_list = []
|
||||
un_auth_list = []
|
||||
listType.value = 'WHITE_LIST'
|
||||
loading.value = true
|
||||
currentType = type
|
||||
KnowledgeApi[`getSharedAuthorization${type}Get`](id)
|
||||
.then((res) => {
|
||||
auth_list = (res.data || {}).auth_list || []
|
||||
un_auth_list = (res.data || {}).un_auth_list || []
|
||||
workspace.value = [...un_auth_list, ...auth_list.map((ele) => ele.workspace_id)]
|
||||
handleListTypeChange(listType.value)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
centerDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
KnowledgeApi[`getSharedAuthorization${currentType}Post`](knowledge_id, {
|
||||
workspace_id_list: checkedWorkspace.value,
|
||||
authentication_type: listType.value,
|
||||
}).then(() => {
|
||||
centerDialogVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const clearWorkspace = (val) => {
|
||||
checkedWorkspace.value = checkedWorkspace.value.filter((ele) => ele !== val)
|
||||
auth_list = auth_list.filter((ele) => ele.workspace_id !== val)
|
||||
}
|
||||
|
||||
const clearWorkspaceAll = () => {
|
||||
checkedWorkspace.value = []
|
||||
auth_list = auth_list.filter((ele) => ele.authentication_type !== listType.value)
|
||||
handleCheckedWorkspaceChange([])
|
||||
}
|
||||
|
||||
const handleListTypeChange = (val) => {
|
||||
checkedWorkspace.value = auth_list
|
||||
.filter((ele) => ele.authentication_type === val)
|
||||
.map((ele) => ele.workspace_id)
|
||||
handleCheckedWorkspaceChange(checkedWorkspace.value)
|
||||
}
|
||||
defineExpose({
|
||||
open,
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.authorized-workspace {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
.tip {
|
||||
color: #646a73;
|
||||
}
|
||||
|
||||
.el-dialog__header {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.form-type {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.el-checkbox__label,
|
||||
.el-radio__label {
|
||||
color: #1f2329 !important;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.form-type_work {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.el-radio {
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.workspace-list {
|
||||
width: 100%;
|
||||
border: 1px solid #dee0e3;
|
||||
border-radius: 4px;
|
||||
height: 428px;
|
||||
}
|
||||
|
||||
.to-be_selected {
|
||||
padding: 16px 0;
|
||||
float: left;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
border-right: 1px solid #dee0e3;
|
||||
box-sizing: border-box;
|
||||
|
||||
.el-input {
|
||||
margin: 0 0 12px 16px;
|
||||
}
|
||||
.el-checkbox {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
padding: 22px 0;
|
||||
float: right;
|
||||
width: calc(50% - 1px);
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.count {
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
.el-button {
|
||||
height: 26px;
|
||||
width: 44px;
|
||||
margin-right: 8px;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background-color: #3370ff1a;
|
||||
color: var(--el-button-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-item_primary,
|
||||
.el-checkbox {
|
||||
height: 38px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
.el-icon:not(.close) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.el-checkbox__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 80%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 88%;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:not(.active):hover {
|
||||
background: rgba(31, 35, 41, 0.1);
|
||||
}
|
||||
}
|
||||
.dialog-footer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.el-button {
|
||||
min-width: 80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
<template>
|
||||
<LayoutContainer :header="$t('views.document.importDocument')" class="create-dataset">
|
||||
<template #backButton>
|
||||
<back-button @click="back"></back-button>
|
||||
</template>
|
||||
<div class="create-dataset__main flex" v-loading="loading">
|
||||
<div class="create-dataset__component main-calc-height">
|
||||
<div class="upload-document p-24" style="min-width: 850px">
|
||||
<h4 class="title-decoration-1 mb-8">
|
||||
{{ $t('views.document.feishu.selectDocument') }}
|
||||
</h4>
|
||||
<el-form
|
||||
ref="FormRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
<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-group>
|
||||
</div>
|
||||
<div class="update-info flex p-8-12 border-r-4 mb-16">
|
||||
<div class="mt-4">
|
||||
<AppIcon iconName="app-warning-colorful" style="font-size: 16px"></AppIcon>
|
||||
</div>
|
||||
<div class="ml-16 lighter">
|
||||
<p>{{ $t('views.document.feishu.tip1') }}</p>
|
||||
<p>{{ $t('views.document.feishu.tip2') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-never border-r-4 mb-16">
|
||||
<el-checkbox
|
||||
v-model="allCheck"
|
||||
:label="$t('views.document.feishu.allCheck')"
|
||||
size="large"
|
||||
class="ml-24"
|
||||
@change="handleAllCheckChange"
|
||||
/>
|
||||
</div>
|
||||
<div style="height: calc(100vh - 450px)">
|
||||
<el-scrollbar>
|
||||
<el-tree
|
||||
:props="props"
|
||||
:load="loadNode"
|
||||
lazy
|
||||
show-checkbox
|
||||
node-key="token"
|
||||
ref="treeRef"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<div class="custom-tree-node flex align-center lighter">
|
||||
<img
|
||||
src="@/assets/fileType/file-icon.svg"
|
||||
alt=""
|
||||
height="20"
|
||||
v-if="data.type === 'folder'"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/fileType/docx-icon.svg"
|
||||
alt=""
|
||||
height="22"
|
||||
v-else-if="data.type === 'docx' || data.name.endsWith('.docx')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/fileType/xlsx-icon.svg"
|
||||
alt=""
|
||||
height="22"
|
||||
v-else-if="data.type === 'sheet' || data.name.endsWith('.xlsx')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/fileType/xls-icon.svg"
|
||||
alt=""
|
||||
height="22"
|
||||
v-else-if="data.name.endsWith('xls')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/fileType/csv-icon.svg"
|
||||
alt=""
|
||||
height="22"
|
||||
v-else-if="data.name.endsWith('csv')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/fileType/pdf-icon.svg"
|
||||
alt=""
|
||||
height="22"
|
||||
v-else-if="data.name.endsWith('.pdf')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/fileType/html-icon.svg"
|
||||
alt=""
|
||||
height="22"
|
||||
v-else-if="data.name.endsWith('.html')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/fileType/txt-icon.svg"
|
||||
alt=""
|
||||
height="22"
|
||||
v-else-if="data.name.endsWith('.txt')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/fileType/zip-icon.svg"
|
||||
alt=""
|
||||
height="22"
|
||||
v-else-if="data.name.endsWith('.zip')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/fileType/md-icon.svg"
|
||||
alt=""
|
||||
height="22"
|
||||
v-else-if="data.name.endsWith('.md')"
|
||||
/>
|
||||
|
||||
<span class="ml-4">{{ node.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="create-dataset__footer text-right border-t">
|
||||
<el-button @click="router.go(-1)">{{ $t('common.cancel') }}</el-button>
|
||||
|
||||
<el-button @click="submit" type="primary" :disabled="disabled">
|
||||
{{ $t('views.document.buttons.import') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</LayoutContainer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onUnmounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { MsgConfirm, MsgSuccess, MsgWarning } from '@/utils/message'
|
||||
import { getImgUrl } from '@/utils/utils'
|
||||
import { t } from '@/locales'
|
||||
import type Node from 'element-plus/es/components/tree/src/model/node'
|
||||
import knowledgeApi from '@/api/shared/knowledge'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const {
|
||||
query: { id, folder_token } // id为knowledgeID,有id的是上传文档 folder_token为飞书文件夹token
|
||||
} = route
|
||||
const knowledgeId = 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
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
fileList: [
|
||||
{ required: true, message: t('views.document.upload.requiredMessage'), trigger: 'change' }
|
||||
]
|
||||
})
|
||||
|
||||
const props = {
|
||||
label: 'name',
|
||||
children: 'zones',
|
||||
isLeaf: (data: any) => data.type !== 'folder',
|
||||
disabled: (data: any) => data.is_exist
|
||||
}
|
||||
|
||||
const loadNode = (node: Node, resolve: (nodeData: Tree[]) => void) => {
|
||||
const token = node.level === 0 ? folderToken : node.data.token // 根节点使用 folder_token,其他节点使用 node.data.token
|
||||
knowledgeApi
|
||||
.getLarkDocumentList(knowledgeId, token, {}, loading)
|
||||
.then((res: any) => {
|
||||
const nodes = res.data.files as Tree[]
|
||||
resolve(nodes)
|
||||
nodes.forEach((childNode) => {
|
||||
if (childNode.is_exist) {
|
||||
treeRef.value?.setChecked(childNode.token, true, false)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
.catch((err) => {
|
||||
console.error('Failed to load tree nodes:', err)
|
||||
})
|
||||
}
|
||||
|
||||
const handleAllCheckChange = (checked: boolean) => {
|
||||
if (checked) {
|
||||
// 获取所有已加载的节点
|
||||
const nodes = Object.values(treeRef.value?.store.nodesMap || {}) as any[]
|
||||
nodes.forEach((node) => {
|
||||
// 只选择未禁用且是文件的节点
|
||||
if (!node.disabled) {
|
||||
treeRef.value?.setChecked(node.data, true, false)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
treeRef.value?.setCheckedKeys([])
|
||||
}
|
||||
}
|
||||
|
||||
function submit() {
|
||||
loading.value = true
|
||||
disabled.value = true
|
||||
// 选中的节点的token
|
||||
const checkedNodes = treeRef.value?.getCheckedNodes() || []
|
||||
const filteredNodes = checkedNodes.filter((node: any) => !node.is_exist)
|
||||
const newList = filteredNodes.map((node: any) => {
|
||||
return {
|
||||
name: node.name,
|
||||
token: node.token,
|
||||
type: node.type
|
||||
}
|
||||
})
|
||||
if (newList.length === 0) {
|
||||
disabled.value = false
|
||||
MsgWarning(t('views.document.feishu.errorMessage1'))
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
knowledge
|
||||
.importLarkDocument(knowledgeId, newList, loading)
|
||||
.then((res) => {
|
||||
MsgSuccess(t('views.document.tip.importMessage'))
|
||||
disabled.value = false
|
||||
router.go(-1)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to load tree nodes:', err)
|
||||
})
|
||||
.finally(() => {
|
||||
disabled.value = false
|
||||
})
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
function back() {
|
||||
router.go(-1)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.create-dataset {
|
||||
&__component {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
padding: 16px 24px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #ffffff;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.upload-document {
|
||||
width: 70%;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.xlsx-icon {
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
stroke: #000000 !important;
|
||||
fill: #ffffff !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
<template>
|
||||
<div class="p-16-24">
|
||||
<h2 class="mb-16">{{ $t('common.setting') }}</h2>
|
||||
<el-card style="--el-card-padding: 0">
|
||||
<div class="knowledge-setting main-calc-height">
|
||||
<el-scrollbar>
|
||||
<div class="p-24" v-loading="loading">
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('common.info') }}
|
||||
</h4>
|
||||
<BaseForm ref="BaseFormRef" :data="detail" />
|
||||
|
||||
<el-form
|
||||
ref="webFormRef"
|
||||
:rules="rules"
|
||||
:model="form"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
<el-form-item :label="$t('views.knowledge.knowledgeType.label')" required>
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-8 w-full"
|
||||
style="line-height: 22px"
|
||||
v-if="detail.type === 0"
|
||||
>
|
||||
<div class="flex align-center">
|
||||
<el-avatar class="mr-8 avatar-blue" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_document.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<div>
|
||||
<div>{{ $t('views.knowledge.knowledgeType.generalKnowledge') }}</div>
|
||||
<el-text type="info"
|
||||
>{{ $t('views.knowledge.knowledgeType.generalInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-8 w-full"
|
||||
style="line-height: 22px"
|
||||
v-if="detail?.type === 1"
|
||||
>
|
||||
<div class="flex align-center">
|
||||
<el-avatar class="mr-8 avatar-purple" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_web.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<div>
|
||||
<div>{{ $t('views.knowledge.knowledgeType.webKnowledge') }}</div>
|
||||
<el-text type="info">
|
||||
{{ $t('views.knowledge.knowledgeType.webInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-8 w-full"
|
||||
style="line-height: 22px"
|
||||
v-if="detail?.type === 2"
|
||||
>
|
||||
<div class="flex align-center">
|
||||
<el-avatar shape="square" :size="32" style="background: none">
|
||||
<img src="@/assets/knowledge/logo_lark.svg" style="width: 100%" alt="" />
|
||||
</el-avatar>
|
||||
<div>
|
||||
<p>
|
||||
<el-text>{{ $t('views.knowledge.knowledgeType.larkKnowledge') }}</el-text>
|
||||
</p>
|
||||
<el-text type="info"
|
||||
>{{ $t('views.knowledge.knowledgeType.larkInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('views.knowledge.form.source_url.label')"
|
||||
prop="source_url"
|
||||
v-if="detail.type === 1"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.source_url"
|
||||
:placeholder="$t('views.knowledge.form.source_url.placeholder')"
|
||||
@blur="form.source_url = form.source_url.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('views.knowledge.form.selector.label')"
|
||||
v-if="detail.type === 1"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.selector"
|
||||
:placeholder="$t('views.knowledge.form.selector.placeholder')"
|
||||
@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"
|
||||
type="password"
|
||||
show-password
|
||||
: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>
|
||||
|
||||
<div v-if="detail.type === 0">
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('common.otherSetting') }}
|
||||
</h4>
|
||||
</div>
|
||||
<el-form-item :label="$t('上传的每个文档最大限制')">
|
||||
<el-slider
|
||||
v-model="form.max_paragraph_char_number"
|
||||
show-input
|
||||
:show-input-controls="false"
|
||||
:min="500"
|
||||
:max="100000"
|
||||
class="custom-slider"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="text-right">
|
||||
<el-button @click="submit" type="primary"> {{ $t('common.save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import BaseForm from '@/views/knowledge-shared-system/component/BaseForm.vue'
|
||||
import KnowledgeApi from '@/api/shared/knowledge'
|
||||
import type { ApplicationFormType } from '@/api/type/application'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import { isAppIcon } from '@/utils/common'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id },
|
||||
} = route as any
|
||||
|
||||
const { knowledge } = useStore()
|
||||
const webFormRef = ref()
|
||||
const BaseFormRef = ref()
|
||||
const loading = ref(false)
|
||||
const detail = ref<any>({})
|
||||
const cloneModelId = ref('')
|
||||
|
||||
const form = ref<any>({
|
||||
source_url: '',
|
||||
selector: '',
|
||||
app_id: '',
|
||||
app_secret: '',
|
||||
folder_token: '',
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
source_url: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.knowledge.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',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
async function submit() {
|
||||
if (await BaseFormRef.value?.validate()) {
|
||||
await webFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
const obj =
|
||||
detail.value.type === 1 || detail.value.type === 2
|
||||
? {
|
||||
meta: form.value,
|
||||
...BaseFormRef.value.form,
|
||||
}
|
||||
: {
|
||||
...BaseFormRef.value.form,
|
||||
}
|
||||
|
||||
if (cloneModelId.value !== BaseFormRef.value.form.embedding_mode_id) {
|
||||
MsgConfirm(t('common.tip'), t('views.knowledge.tip.updateModeMessage'), {
|
||||
confirmButtonText: t('views.knowledge.setting.vectorization'),
|
||||
})
|
||||
.then(() => {
|
||||
if (detail.value.type === 2) {
|
||||
KnowledgeApi.putLarkKnowledge(id, obj, loading).then((res) => {
|
||||
KnowledgeApi.putReEmbeddingKnowledge(id).then(() => {
|
||||
MsgSuccess(t('common.saveSuccess'))
|
||||
})
|
||||
})
|
||||
} else {
|
||||
KnowledgeApi.putKnowledge(id, obj, loading).then((res) => {
|
||||
KnowledgeApi.putReEmbeddingKnowledge(id).then(() => {
|
||||
MsgSuccess(t('common.saveSuccess'))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
} else {
|
||||
if (detail.value.type === 2) {
|
||||
KnowledgeApi.putLarkKnowledge(id, obj, loading).then((res) => {
|
||||
KnowledgeApi.putReEmbeddingKnowledge(id).then(() => {
|
||||
MsgSuccess(t('common.saveSuccess'))
|
||||
})
|
||||
})
|
||||
} else {
|
||||
KnowledgeApi.putKnowledge(id, obj, loading).then((res) => {
|
||||
MsgSuccess(t('common.saveSuccess'))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getDetail() {
|
||||
knowledge.asyncGetKnowledgeDetail(id, loading).then((res: any) => {
|
||||
detail.value = res.data
|
||||
cloneModelId.value = res.data?.embedding_mode_id
|
||||
if (detail.value.type === '1' || detail.value.type === '2') {
|
||||
form.value = res.data.meta
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.knowledge-setting {
|
||||
width: 70%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,461 @@
|
|||
<template>
|
||||
<div class="knowledge-shared">
|
||||
<ContentContainer :header="$t('views.system.share_knowledge')">
|
||||
<template #search>
|
||||
<div class="flex">
|
||||
<div class="flex-between complex-search">
|
||||
<el-select
|
||||
class="complex-search__left"
|
||||
v-model="search_type"
|
||||
style="width: 120px"
|
||||
@change="search_type_change"
|
||||
>
|
||||
<el-option :label="$t('common.creator')" value="create_user" />
|
||||
|
||||
<el-option :label="$t('common.name')" value="name" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-if="search_type === 'name'"
|
||||
v-model="search_form.name"
|
||||
@change="getList"
|
||||
:placeholder="$t('common.searchBar.placeholder')"
|
||||
style="width: 220px"
|
||||
clearable
|
||||
/>
|
||||
<el-select
|
||||
v-else-if="search_type === 'create_user'"
|
||||
v-model="search_form.create_user"
|
||||
@change="getList"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option v-for="u in user_options" :key="u.id" :value="u.id" :label="u.username" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="primary" class="ml-8">
|
||||
{{ $t('common.create') }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="create-dropdown">
|
||||
<el-dropdown-item @click="openCreateDialog(CreateKnowledgeDialog)">
|
||||
<div class="flex">
|
||||
<el-avatar class="avatar-blue mt-4" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_document.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<div class="pre-wrap ml-8">
|
||||
<div class="lighter">
|
||||
{{ $t('views.knowledge.knowledgeType.generalKnowledge') }}
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>{{ $t('views.knowledge.knowledgeType.generalInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="openCreateDialog(CreateWebKnowledgeDialog)">
|
||||
<div class="flex">
|
||||
<el-avatar class="avatar-purple mt-4" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_web.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<div class="pre-wrap ml-8">
|
||||
<div class="lighter">
|
||||
{{ $t('views.knowledge.knowledgeType.webKnowledge') }}
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>{{ $t('views.knowledge.knowledgeType.webInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<div class="flex">
|
||||
<el-avatar
|
||||
class="avatar-purple mt-4"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img src="@/assets/knowledge/logo_lark.svg" alt="" />
|
||||
</el-avatar>
|
||||
<div class="pre-wrap ml-8">
|
||||
<div class="lighter">
|
||||
{{ $t('views.knowledge.knowledgeType.larkKnowledge') }}
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>{{ $t('views.knowledge.knowledgeType.larkInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<div class="flex">
|
||||
<el-avatar
|
||||
class="avatar-purple mt-4"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img src="@/assets/knowledge/logo_yuque.svg" alt="" />
|
||||
</el-avatar>
|
||||
<div class="pre-wrap ml-8">
|
||||
<div class="lighter">
|
||||
{{ $t('views.knowledge.knowledgeType.yuqueKnowledge') }}
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>{{ $t('views.knowledge.knowledgeType.yuqueInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<div v-loading.fullscreen.lock="paginationConfig.current_page === 1 && loading">
|
||||
<InfiniteScroll
|
||||
:size="knowledgeList.length"
|
||||
:total="paginationConfig.total"
|
||||
:page_size="paginationConfig.page_size"
|
||||
v-model:current_page="paginationConfig.current_page"
|
||||
@load="getList"
|
||||
:loading="loading"
|
||||
>
|
||||
<el-row v-if="knowledgeList.length > 0" :gutter="15">
|
||||
<template v-for="(item, index) in knowledgeList" :key="index">
|
||||
<el-col
|
||||
v-if="item.resource_type === 'folder'"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="12"
|
||||
:lg="8"
|
||||
:xl="6"
|
||||
class="mb-16"
|
||||
>
|
||||
<CardBox
|
||||
:title="item.name"
|
||||
:description="item.desc || $t('common.noData')"
|
||||
class="cursor"
|
||||
>
|
||||
<template #icon>
|
||||
<el-avatar shape="square" :size="32" style="background: none">
|
||||
<AppIcon iconName="app-folder" style="font-size: 32px"></AppIcon>
|
||||
</el-avatar>
|
||||
</template>
|
||||
<template #subTitle>
|
||||
<el-text class="color-secondary lighter" size="small">
|
||||
{{ $t('common.creator') }}: {{ item.username }}
|
||||
</el-text>
|
||||
</template>
|
||||
</CardBox>
|
||||
</el-col>
|
||||
<el-col v-else :xs="24" :sm="12" :md="12" :lg="8" :xl="6" class="mb-16">
|
||||
<CardBox
|
||||
:title="item.name"
|
||||
:description="item.desc"
|
||||
isShared
|
||||
class="cursor"
|
||||
@click="
|
||||
router.push({
|
||||
path: `/knowledge/system/${item.id}/documentShared`,
|
||||
query: {
|
||||
from: 'shared',
|
||||
},
|
||||
})
|
||||
"
|
||||
>
|
||||
<template #icon>
|
||||
<KnowledgeIcon :type="item.type" />
|
||||
</template>
|
||||
<template #subTitle>
|
||||
<el-text class="color-secondary" size="small">
|
||||
{{ $t('common.creator') }}: {{ item.username }}
|
||||
</el-text>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<div>
|
||||
<span class="bold mr-4">{{ item?.document_count || 0 }}</span>
|
||||
<span class="color-secondary">{{
|
||||
$t('views.knowledge.document_count')
|
||||
}}</span>
|
||||
<el-divider direction="vertical" />
|
||||
<span class="bold mr-4">{{ numberFormat(item?.char_length) || 0 }}</span>
|
||||
<span class="color-secondary">{{ $t('common.character') }}</span>
|
||||
<el-divider direction="vertical" />
|
||||
<span class="bold mr-4">{{ item?.application_mapping_count || 0 }}</span>
|
||||
<span class="color-secondary">{{
|
||||
$t('views.knowledge.relatedApp_count')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #mouseEnter>
|
||||
<div @click.stop>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button text @click.stop>
|
||||
<el-icon>
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
icon="Refresh"
|
||||
@click.stop="syncKnowledge(item)"
|
||||
v-if="item.type === 1"
|
||||
>{{ $t('views.knowledge.setting.sync') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.stop="reEmbeddingKnowledge(item)">
|
||||
<AppIcon iconName="app-vectorization"></AppIcon>
|
||||
{{ $t('views.knowledge.setting.vectorization') }}
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item
|
||||
icon="Connection"
|
||||
@click.stop="openGenerateDialog(item)"
|
||||
>{{ $t('views.document.generateQuestion.title') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
icon="Lock"
|
||||
@click.stop="openAuthorizedWorkspaceDialog(item)"
|
||||
>{{ $t('views.system.authorized_workspace') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
icon="Setting"
|
||||
@click.stop="
|
||||
router.push({
|
||||
path: `/knowledge/system/${item.id}/settingShared`,
|
||||
query: {
|
||||
from: 'shared',
|
||||
},
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ $t('common.setting') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click.stop="exportKnowledge(item)">
|
||||
<AppIcon iconName="app-export"></AppIcon
|
||||
>{{ $t('views.document.setting.export') }} Excel</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click.stop="exportZipKnowledge(item)">
|
||||
<AppIcon iconName="app-export"></AppIcon
|
||||
>{{ $t('views.document.setting.export') }} ZIP</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item icon="Delete" @click.stop="deleteKnowledge(item)">{{
|
||||
$t('common.delete')
|
||||
}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</CardBox>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
<el-empty :description="$t('common.noData')" v-else />
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
</ContentContainer>
|
||||
|
||||
<component :is="currentCreateDialog" ref="CreateKnowledgeDialogRef" />
|
||||
<CreateFolderDialog ref="CreateFolderDialogRef" @refresh="refreshFolder" />
|
||||
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" />
|
||||
<AuthorizedWorkspace ref="AuthorizedWorkspaceDialogRef"></AuthorizedWorkspace>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, reactive, shallowRef, nextTick } from 'vue'
|
||||
import KnowledgeIcon from '@/views/knowledge-shared-system/component/KnowledgeIcon.vue'
|
||||
import CreateKnowledgeDialog from './create-component/CreateKnowledgeDialog.vue'
|
||||
import CreateWebKnowledgeDialog from './create-component/CreateWebKnowledgeDialog.vue'
|
||||
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
|
||||
import GenerateRelatedDialog from '@/components/generate-related-dialog/index.vue'
|
||||
import KnowledgeApi from '@/api/shared/knowledge'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import { numberFormat } from '@/utils/common'
|
||||
import iconMap from '@/components/app-icon/icons/common'
|
||||
import { t } from '@/locales'
|
||||
import { useRouter } from 'vue-router'
|
||||
import AuthorizedWorkspace from '@/views/knowledge-shared-system/AuthorizedWorkspace.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const { folder } = useStore()
|
||||
|
||||
const loading = ref(false)
|
||||
const rightOutlined = iconMap['right-outlined'].iconReader()
|
||||
|
||||
const search_type = ref('name')
|
||||
const search_form = ref<any>({
|
||||
name: '',
|
||||
create_user: '',
|
||||
})
|
||||
|
||||
const user_options = ref<any[]>([])
|
||||
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 30,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const folderList = ref<any[]>([])
|
||||
const knowledgeList = ref<any[]>([])
|
||||
const currentFolder = ref<any>({})
|
||||
|
||||
const CreateKnowledgeDialogRef = ref()
|
||||
const currentCreateDialog = shallowRef<any>(null)
|
||||
const AuthorizedWorkspaceDialogRef = ref()
|
||||
function openCreateDialog(data: any) {
|
||||
currentCreateDialog.value = data
|
||||
nextTick(() => {
|
||||
CreateKnowledgeDialogRef.value.open(currentFolder.value)
|
||||
})
|
||||
|
||||
// common.asyncGetValid(ValidType.Dataset, ValidCount.Dataset, loading).then(async (res: any) => {
|
||||
// if (res?.data) {
|
||||
// CreateDatasetDialogRef.value.open()
|
||||
// } else if (res?.code === 400) {
|
||||
// MsgConfirm(t('common.tip'), t('views.knowledge.tip.professionalMessage'), {
|
||||
// cancelButtonText: t('common.confirm'),
|
||||
// confirmButtonText: t('common.professional'),
|
||||
// })
|
||||
// .then(() => {
|
||||
// window.open('https://maxkb.cn/pricing.html', '_blank')
|
||||
// })
|
||||
// .catch(() => {})
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
function reEmbeddingKnowledge(row: any) {
|
||||
KnowledgeApi.putReEmbeddingKnowledge(row.id).then(() => {
|
||||
MsgSuccess(t('common.submitSuccess'))
|
||||
})
|
||||
}
|
||||
|
||||
const SyncWebDialogRef = ref()
|
||||
|
||||
function syncKnowledge(row: any) {
|
||||
SyncWebDialogRef.value.open(row.id)
|
||||
}
|
||||
|
||||
const search_type_change = () => {
|
||||
search_form.value = { name: '', create_user: '' }
|
||||
}
|
||||
|
||||
function getList() {
|
||||
const params = {
|
||||
[search_type.value]: search_form.value[search_type.value],
|
||||
}
|
||||
|
||||
if (!search_form.value[search_type.value]) {
|
||||
delete params[search_type.value]
|
||||
}
|
||||
KnowledgeApi.getSharedWorkspaceKnowledgePage(params, loading).then((res) => {
|
||||
knowledgeList.value = [...res.data]
|
||||
})
|
||||
}
|
||||
|
||||
function getFolder() {
|
||||
const params = {}
|
||||
folder.asyncGetFolder('KNOWLEDGE', params, loading).then((res: any) => {
|
||||
folderList.value = res.data
|
||||
currentFolder.value = res.data?.[0] || {}
|
||||
getList()
|
||||
})
|
||||
}
|
||||
|
||||
function folderClickHandel(row: any) {
|
||||
currentFolder.value = row
|
||||
knowledgeList.value = []
|
||||
getList()
|
||||
}
|
||||
|
||||
const CreateFolderDialogRef = ref()
|
||||
|
||||
function openCreateFolder() {
|
||||
CreateFolderDialogRef.value.open('KNOWLEDGE', currentFolder.value.parent_id)
|
||||
}
|
||||
|
||||
const GenerateRelatedDialogRef = ref<InstanceType<typeof GenerateRelatedDialog>>()
|
||||
function openGenerateDialog(row: any) {
|
||||
if (GenerateRelatedDialogRef.value) {
|
||||
GenerateRelatedDialogRef.value.open([], 'knowledge', row.id)
|
||||
}
|
||||
}
|
||||
|
||||
function openAuthorizedWorkspaceDialog(row: any) {
|
||||
if (AuthorizedWorkspaceDialogRef.value) {
|
||||
AuthorizedWorkspaceDialogRef.value.open(row)
|
||||
}
|
||||
}
|
||||
|
||||
const exportKnowledge = (item: any) => {
|
||||
KnowledgeApi.exportKnowledge(item.name, item.id, loading).then((ok) => {
|
||||
MsgSuccess(t('common.exportSuccess'))
|
||||
})
|
||||
}
|
||||
const exportZipKnowledge = (item: any) => {
|
||||
KnowledgeApi.exportZipKnowledge(item.name, item.id, loading).then((ok) => {
|
||||
MsgSuccess(t('common.exportSuccess'))
|
||||
})
|
||||
}
|
||||
|
||||
function deleteKnowledge(row: any) {
|
||||
MsgConfirm(
|
||||
`${t('views.knowledge.delete.confirmTitle')}${row.name} ?`,
|
||||
`${t('views.knowledge.delete.confirmMessage1')} ${row.application_mapping_count} ${t('views.knowledge.delete.confirmMessage2')}`,
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
KnowledgeApi.delKnowledge(row.id, loading).then(() => {
|
||||
const index = knowledgeList.value.findIndex((v) => v.id === row.id)
|
||||
knowledgeList.value.splice(index, 1)
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function refreshFolder() {
|
||||
getFolder()
|
||||
getList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.knowledge-shared {
|
||||
padding-left: 8px;
|
||||
.shared-header {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:deep(.el-icon i) {
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
color: #1f2329;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
<template>
|
||||
<el-form
|
||||
ref="FormRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-form-item :label="$t('views.knowledge.form.knowledgeName.label')" prop="name">
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
:placeholder="$t('views.knowledge.form.knowledgeName.placeholder')"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
@blur="form.name = form.name.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.knowledge.form.knowledgeDescription.label')" prop="desc">
|
||||
<el-input
|
||||
v-model="form.desc"
|
||||
type="textarea"
|
||||
:placeholder="$t('views.knowledge.form.knowledgeDescription.placeholder')"
|
||||
maxlength="256"
|
||||
show-word-limit
|
||||
:autosize="{ minRows: 3 }"
|
||||
@blur="form.desc = form.desc.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('views.knowledge.form.EmbeddingModel.label')"
|
||||
prop="embedding_model_id"
|
||||
>
|
||||
<ModelSelect
|
||||
v-model="form.embedding"
|
||||
:placeholder="$t('views.knowledge.form.EmbeddingModel.placeholder')"
|
||||
:options="modelOptions"
|
||||
:model-type="'EMBEDDING'"
|
||||
showFooter
|
||||
></ModelSelect>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, onUnmounted, computed, watch } from 'vue'
|
||||
import { groupBy } from 'lodash'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import type { knowledgeData } from '@/api/type/knowledge'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
})
|
||||
const { model } = useStore()
|
||||
const form = ref<knowledgeData>({
|
||||
name: '',
|
||||
desc: '',
|
||||
embedding: '',
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.knowledge.form.knowledgeName.requiredMessage'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
desc: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.knowledge.form.knowledgeDescription.requiredMessage'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
embedding: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.knowledge.form.EmbeddingModel.requiredMessage'),
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const FormRef = ref()
|
||||
const loading = ref(false)
|
||||
const modelOptions = ref<any>([])
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(value) => {
|
||||
if (value && JSON.stringify(value) !== '{}') {
|
||||
form.value.name = value.name
|
||||
form.value.desc = value.desc
|
||||
form.value.embedding = value.embedding
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
|
||||
/*
|
||||
表单校验
|
||||
*/
|
||||
function validate() {
|
||||
if (!FormRef.value) return
|
||||
return FormRef.value.validate((valid: any) => {
|
||||
return valid
|
||||
})
|
||||
}
|
||||
|
||||
function getModel() {
|
||||
loading.value = true
|
||||
model
|
||||
.asyncGetModel({ model_type: 'EMBEDDING' })
|
||||
.then((res: any) => {
|
||||
modelOptions.value = groupBy(res?.data, 'provider')
|
||||
loading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getModel()
|
||||
})
|
||||
onUnmounted(() => {
|
||||
form.value = {
|
||||
name: '',
|
||||
desc: '',
|
||||
embedding: '',
|
||||
}
|
||||
FormRef.value?.clearValidate()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
form,
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.paragraph.editParagraph')"
|
||||
v-model="dialogVisible"
|
||||
width="80%"
|
||||
destroy-on-close
|
||||
class="paragraph-dialog"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<el-row v-if="isConnect">
|
||||
<el-col :span="18" class="p-24">
|
||||
<ParagraphForm ref="paragraphFormRef" :data="detail" :isEdit="true" />
|
||||
</el-col>
|
||||
<el-col :span="6" class="border-l" style="width: 300px">
|
||||
<p class="bold title p-24" style="padding-bottom: 0">
|
||||
<span class="flex align-center">
|
||||
<span>{{ $t('views.paragraph.relatedProblem.title') }}</span>
|
||||
<el-divider direction="vertical" class="mr-4" />
|
||||
<el-button text @click="addProblem">
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-button>
|
||||
</span>
|
||||
</p>
|
||||
<el-scrollbar height="500px">
|
||||
<div class="p-24" style="padding-top: 16px">
|
||||
<el-input
|
||||
v-if="isAddProblem"
|
||||
v-model="problemValue"
|
||||
:placeholder="$t('views.paragraph.relatedProblem.placeholder')"
|
||||
@change="addProblemHandle"
|
||||
@blur="isAddProblem = false"
|
||||
ref="inputRef"
|
||||
class="mb-8"
|
||||
/>
|
||||
|
||||
<template v-for="(item, index) in detail.problem_list" :key="index">
|
||||
<TagEllipsis
|
||||
@close="delProblemHandle(item, index)"
|
||||
class="question-tag"
|
||||
type="info"
|
||||
effect="plain"
|
||||
closable
|
||||
>
|
||||
<auto-tooltip :content="item.content">
|
||||
{{ item.content }}
|
||||
</auto-tooltip>
|
||||
</TagEllipsis>
|
||||
</template>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div v-else class="p-24">
|
||||
<ParagraphForm ref="paragraphFormRef" :data="detail" :isEdit="true" />
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submitHandle"> {{ $t('common.save') }} </el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, nextTick } from 'vue'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import ParagraphForm from '@/views/paragraph/component/ParagraphForm.vue'
|
||||
|
||||
const props = defineProps({
|
||||
isConnect: Boolean
|
||||
})
|
||||
|
||||
const emit = defineEmits(['updateContent'])
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
const detail = ref<any>({})
|
||||
|
||||
const paragraphFormRef = ref()
|
||||
const inputRef = ref()
|
||||
|
||||
const isAddProblem = ref(false)
|
||||
|
||||
const problemValue = ref('')
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
detail.value = {}
|
||||
}
|
||||
})
|
||||
|
||||
const open = (data: any) => {
|
||||
detail.value = cloneDeep(data)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function delProblemHandle(item: any, index: number) {
|
||||
detail.value.problem_list.splice(index, 1)
|
||||
}
|
||||
function addProblemHandle() {
|
||||
if (problemValue.value.trim()) {
|
||||
if (
|
||||
!detail.value?.problem_list.some((item: any) => item.content === problemValue.value.trim())
|
||||
) {
|
||||
detail.value?.problem_list?.push({
|
||||
content: problemValue.value.trim()
|
||||
})
|
||||
}
|
||||
|
||||
problemValue.value = ''
|
||||
isAddProblem.value = false
|
||||
}
|
||||
}
|
||||
function addProblem() {
|
||||
isAddProblem.value = true
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus()
|
||||
})
|
||||
}
|
||||
|
||||
const submitHandle = async () => {
|
||||
if (await paragraphFormRef.value?.validate()) {
|
||||
emit('updateContent', {
|
||||
problem_list: detail.value.problem_list,
|
||||
...paragraphFormRef.value?.form
|
||||
})
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<el-avatar v-if="type === 1" class="avatar-purple" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_web.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<el-avatar
|
||||
v-else-if="type === 2"
|
||||
class="avatar-purple"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img src="@/assets/knowledge/logo_lark.svg" style="width: 100%" alt="" />
|
||||
</el-avatar>
|
||||
<el-avatar v-else class="avatar-blue" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_document.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<template>
|
||||
<div>
|
||||
<InfiniteScroll
|
||||
:size="paragraph_list.length"
|
||||
:total="modelValue.length"
|
||||
:page_size="page_size"
|
||||
v-model:current_page="current_page"
|
||||
@load="next()"
|
||||
:loading="loading"
|
||||
>
|
||||
<el-card
|
||||
v-for="(child, cIndex) in paragraph_list"
|
||||
:key="cIndex"
|
||||
shadow="never"
|
||||
class="card-never mb-16"
|
||||
>
|
||||
<div class="flex-between">
|
||||
<span>{{ child.title || '-' }}</span>
|
||||
<div>
|
||||
<!-- 编辑分段按钮 -->
|
||||
<el-button link @click="editHandle(child, cIndex)">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
</el-button>
|
||||
<!-- 删除分段按钮 -->
|
||||
<el-button link @click="deleteHandle(child, cIndex)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lighter mt-12">
|
||||
{{ child.content }}
|
||||
</div>
|
||||
<div class="lighter mt-12">
|
||||
<el-text type="info">
|
||||
{{ child.content.length }} {{ $t('views.paragraph.character_count') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</el-card>
|
||||
</InfiniteScroll>
|
||||
|
||||
<EditParagraphDialog
|
||||
ref="EditParagraphDialogRef"
|
||||
@updateContent="updateContent"
|
||||
:isConnect="isConnect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { ref, computed } from 'vue'
|
||||
import EditParagraphDialog from './EditParagraphDialog.vue'
|
||||
import { MsgConfirm } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
const page_size = ref<number>(30)
|
||||
const current_page = ref<number>(1)
|
||||
const currentCIndex = ref<number>(0)
|
||||
const EditParagraphDialogRef = ref()
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const loading = ref<boolean>(false)
|
||||
const editHandle = (item: any, cIndex: number) => {
|
||||
currentCIndex.value = cIndex
|
||||
EditParagraphDialogRef.value.open(item)
|
||||
}
|
||||
|
||||
const props = defineProps<{ modelValue: Array<any>; isConnect: boolean }>()
|
||||
|
||||
const paragraph_list = computed(() => {
|
||||
return props.modelValue.slice(0, page_size.value * (current_page.value - 1) + page_size.value)
|
||||
})
|
||||
|
||||
const next = () => {
|
||||
loading.value = true
|
||||
current_page.value += 1
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const updateContent = (data: any) => {
|
||||
const new_value = [...props.modelValue]
|
||||
if (
|
||||
props.isConnect &&
|
||||
data.title &&
|
||||
!data?.problem_list.some((item: any) => item.content === data.title.trim())
|
||||
) {
|
||||
data['problem_list'].push({
|
||||
content: data.title.trim()
|
||||
})
|
||||
}
|
||||
new_value[currentCIndex.value] = cloneDeep(data)
|
||||
emit('update:modelValue', new_value)
|
||||
}
|
||||
|
||||
const deleteHandle = (item: any, cIndex: number) => {
|
||||
MsgConfirm(
|
||||
`${t('views.paragraph.delete.confirmTitle')}${item.title || '-'} ?`,
|
||||
t('views.paragraph.delete.confirmMessage'),
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger'
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
const new_value = [...props.modelValue]
|
||||
new_value.splice(cIndex, 1)
|
||||
emit('update:modelValue', new_value)
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<el-tabs v-model="activeName" class="paragraph-tabs">
|
||||
<template v-for="(item, index) in data" :key="index">
|
||||
<el-tab-pane :label="item.name" :name="index">
|
||||
<template #label>
|
||||
<div class="flex-center">
|
||||
<img :src="getImgUrl(item && item?.name)" alt="" height="16" />
|
||||
<span class="ml-4">{{ item?.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="mb-16">
|
||||
<el-text type="info"
|
||||
>{{ item.content.length }} {{ $t('views.paragraph.title') }}</el-text
|
||||
>
|
||||
</div>
|
||||
<div class="paragraph-list" v-if="activeName == index">
|
||||
<el-scrollbar>
|
||||
<ParagraphList v-model="item.content" :isConnect="isConnect"> </ParagraphList>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</template>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { getImgUrl } from '@/utils/utils'
|
||||
import ParagraphList from './ParagraphList.vue'
|
||||
|
||||
defineProps({
|
||||
data: {
|
||||
type: Array<any>,
|
||||
default: () => []
|
||||
},
|
||||
isConnect: Boolean
|
||||
})
|
||||
|
||||
const activeName = ref(0)
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.paragraph-tabs {
|
||||
:deep(.el-tabs__item) {
|
||||
background: var(--app-text-color-light-1);
|
||||
margin: 4px;
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px 5px 8px !important;
|
||||
height: auto;
|
||||
&:nth-child(2) {
|
||||
margin-left: 0;
|
||||
}
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
&.is-active {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
}
|
||||
:deep(.el-tabs__nav-wrap::after) {
|
||||
display: none;
|
||||
}
|
||||
:deep(.el-tabs__active-bar) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.paragraph-list {
|
||||
height: calc(var(--create-dataset-height) - 101px);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.knowledge.syncWeb.title')"
|
||||
v-model="dialogVisible"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<p class="mb-8">{{ $t('views.knowledge.syncWeb.syncMethod') }}</p>
|
||||
<el-radio-group v-model="method" class="card__radio">
|
||||
<el-card shadow="never" class="mb-16" :class="method === 'replace' ? 'active' : ''">
|
||||
<el-radio value="replace" size="large">
|
||||
<p class="mb-4">{{ $t('views.knowledge.syncWeb.replace') }}</p>
|
||||
<el-text type="info">{{ $t('views.knowledge.syncWeb.replaceText') }}</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mb-16" :class="method === 'complete' ? 'active' : ''">
|
||||
<el-radio value="complete" size="large">
|
||||
<p class="mb-4">{{ $t('views.knowledge.syncWeb.complete') }}</p>
|
||||
<el-text type="info">{{ $t('views.knowledge.syncWeb.completeText') }}</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
</el-radio-group>
|
||||
<p class="danger">{{ $t('views.knowledge.syncWeb.tip') }}</p>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit" :loading="loading">
|
||||
{{ $t('common.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
const { knowledge } = useStore()
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
const loading = ref<boolean>(false)
|
||||
const method = ref('replace')
|
||||
const knowledgeId = ref('')
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
method.value = 'replace'
|
||||
}
|
||||
})
|
||||
|
||||
const open = (id: string) => {
|
||||
knowledgeId.value = id
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const submit = () => {
|
||||
knowledge.asyncSyncKnowledge(knowledgeId.value, method.value, loading).then((res: any) => {
|
||||
emit('refresh', res.data)
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.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>
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.knowledge.knowledgeType.createGeneralKnowledge')"
|
||||
v-model="dialogVisible"
|
||||
width="720"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<!-- 基本信息 -->
|
||||
<BaseForm ref="BaseFormRef" v-if="dialogVisible" />
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false" :loading="loading">
|
||||
{{ $t('common.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitHandle" :loading="loading">
|
||||
{{ $t('common.create') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import BaseForm from '@/views/knowledge-shared-system/component/BaseForm.vue'
|
||||
import KnowledgeApi from '@/api/shared/knowledge'
|
||||
import { MsgSuccess, MsgAlert } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const router = useRouter()
|
||||
const BaseFormRef = ref()
|
||||
|
||||
const loading = ref(false)
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const currentFolder = ref<any>(null)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
currentFolder.value = null
|
||||
}
|
||||
})
|
||||
|
||||
const open = (folder: string) => {
|
||||
currentFolder.value = folder
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const submitHandle = async () => {
|
||||
if (await BaseFormRef.value?.validate()) {
|
||||
const obj = {
|
||||
folder_id: currentFolder.value?.id,
|
||||
...BaseFormRef.value.form,
|
||||
}
|
||||
KnowledgeApi.postKnowledge(obj, loading).then((res) => {
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
// router.push({ path: `/knowledge/${res.data.id}/${currentFolder.value.id}/document` })
|
||||
emit('refresh')
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.knowledge.knowledgeType.createGeneralKnowledge')"
|
||||
v-model="dialogVisible"
|
||||
width="720"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<!-- 基本信息 -->
|
||||
<BaseForm ref="BaseFormRef" v-if="dialogVisible" />
|
||||
<el-form
|
||||
ref="DatasetFormRef"
|
||||
:rules="rules"
|
||||
:model="datasetForm"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
|
||||
<el-form-item
|
||||
:label="$t('views.knowledge.form.source_url.label')"
|
||||
prop="source_url"
|
||||
v-if="datasetForm.type === '1'"
|
||||
>
|
||||
<el-input
|
||||
v-model="datasetForm.source_url"
|
||||
:placeholder="$t('views.knowledge.form.source_url.placeholder')"
|
||||
@blur="datasetForm.source_url = datasetForm.source_url.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('views.knowledge.form.selector.label')"
|
||||
v-if="datasetForm.type === '1'"
|
||||
>
|
||||
<el-input
|
||||
v-model="datasetForm.selector"
|
||||
:placeholder="$t('views.knowledge.form.selector.placeholder')"
|
||||
@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_secret" v-if="datasetForm.type === '2'">
|
||||
<el-input
|
||||
v-model="datasetForm.app_secret"
|
||||
type="password"
|
||||
show-password
|
||||
: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">
|
||||
<el-button @click.prevent="dialogVisible = false" :loading="loading">
|
||||
{{ $t('common.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitHandle" :loading="loading">
|
||||
{{ $t('common.create') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import BaseForm from '@/views/knowledge-shared-system/component/BaseForm.vue'
|
||||
import KnowledgeApi from '@/api/shared/knowledge'
|
||||
import { MsgSuccess, MsgAlert } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
import { ComplexPermission } from '@/utils/permission/type'
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const router = useRouter()
|
||||
const BaseFormRef = ref()
|
||||
const DatasetFormRef = ref()
|
||||
|
||||
const loading = ref(false)
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
const datasetForm = ref<any>({
|
||||
type: '0',
|
||||
source_url: '',
|
||||
selector: '',
|
||||
app_id: '',
|
||||
app_secret: '',
|
||||
folder_token: ''
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
source_url: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.knowledge.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.knowledge.form.user_id.requiredMessage'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
token: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.knowledge.form.token.requiredMessage'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
datasetForm.value = {
|
||||
type: '0',
|
||||
source_url: '',
|
||||
selector: ''
|
||||
}
|
||||
DatasetFormRef.value?.clearValidate()
|
||||
}
|
||||
})
|
||||
|
||||
const open = () => {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const submitHandle = async () => {
|
||||
if (await BaseFormRef.value?.validate()) {
|
||||
await DatasetFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
if (datasetForm.value.type === '0') {
|
||||
const obj = {
|
||||
...BaseFormRef.value.form,
|
||||
type: datasetForm.value.type
|
||||
}
|
||||
knowledgeApi.postDataset(obj, loading).then((res) => {
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
router.push({ path: `/knowledge/system/${res.data.id}/documentShared` })
|
||||
emit('refresh')
|
||||
})
|
||||
} else if (datasetForm.value.type === '1') {
|
||||
const obj = { ...BaseFormRef.value.form, ...datasetForm.value }
|
||||
knowledgeApi.postWebDataset(obj, loading).then((res) => {
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
router.push({ path: `/knowledge/system/${res.data.id}/documentShared` })
|
||||
emit('refresh')
|
||||
})
|
||||
} else if (datasetForm.value.type === '2') {
|
||||
const obj = { ...BaseFormRef.value.form, ...datasetForm.value }
|
||||
knowledgeApi.postLarkDataset(obj, loading).then((res) => {
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
router.push({ path: `/knowledge/system/${res.data.id}/documentShared` })
|
||||
emit('refresh')
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
function radioChange() {
|
||||
datasetForm.value.source_url = ''
|
||||
datasetForm.value.selector = ''
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.knowledge.knowledgeType.createGeneralKnowledge')"
|
||||
v-model="dialogVisible"
|
||||
width="720"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<!-- 基本信息 -->
|
||||
<BaseForm ref="BaseFormRef" v-if="dialogVisible" />
|
||||
<el-form
|
||||
ref="KnowledgeFormRef"
|
||||
:rules="rules"
|
||||
:model="knowledgeForm"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
<el-form-item :label="$t('views.knowledge.form.source_url.label')" prop="source_url">
|
||||
<el-input
|
||||
v-model="knowledgeForm.source_url"
|
||||
:placeholder="$t('views.knowledge.form.source_url.placeholder')"
|
||||
@blur="knowledgeForm.source_url = knowledgeForm.source_url.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.knowledge.form.selector.label')">
|
||||
<el-input
|
||||
v-model="knowledgeForm.selector"
|
||||
:placeholder="$t('views.knowledge.form.selector.placeholder')"
|
||||
@blur="knowledgeForm.selector = knowledgeForm.selector.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false" :loading="loading">
|
||||
{{ $t('common.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitHandle" :loading="loading">
|
||||
{{ $t('common.create') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import BaseForm from '@/views/knowledge-shared-system/component/BaseForm.vue'
|
||||
import KnowledgeApi from '@/api/shared/knowledge'
|
||||
import { MsgSuccess, MsgAlert } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const router = useRouter()
|
||||
const BaseFormRef = ref()
|
||||
const KnowledgeFormRef = ref()
|
||||
|
||||
const loading = ref(false)
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
const knowledgeForm = ref<any>({
|
||||
source_url: '',
|
||||
selector: '',
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
source_url: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.knowledge.form.source_url.requiredMessage'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const currentFolder = ref<any>(null)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
currentFolder.value = null
|
||||
knowledgeForm.value = {
|
||||
source_url: '',
|
||||
selector: '',
|
||||
}
|
||||
KnowledgeFormRef.value?.clearValidate()
|
||||
}
|
||||
})
|
||||
|
||||
const open = (folder: string) => {
|
||||
currentFolder.value = folder
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const submitHandle = async () => {
|
||||
if (await BaseFormRef.value?.validate()) {
|
||||
await KnowledgeFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
const obj = {
|
||||
folder_id: currentFolder.value?.id,
|
||||
...BaseFormRef.value.form,
|
||||
...knowledgeForm.value,
|
||||
}
|
||||
KnowledgeApi.postWebKnowledge(obj, loading).then((res) => {
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
router.push({
|
||||
path: `/knowledge/system/${res.data.id}/documentShared`,
|
||||
query: {
|
||||
from: 'shared',
|
||||
},
|
||||
})
|
||||
emit('refresh')
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,469 @@
|
|||
<template>
|
||||
<div class="knowledge-shared">
|
||||
<ContentContainer>
|
||||
<template #header>
|
||||
<div class="shared-header">
|
||||
<span class="title">{{ t('views.system.shared_resources') }}</span>
|
||||
<el-icon size="12">
|
||||
<rightOutlined></rightOutlined>
|
||||
</el-icon>
|
||||
<span class="sub-title">{{ t('views.knowledge.title') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #search>
|
||||
<div class="flex">
|
||||
<div class="flex-between complex-search">
|
||||
<el-select
|
||||
class="complex-search__left"
|
||||
v-model="search_type"
|
||||
style="width: 120px"
|
||||
@change="search_type_change"
|
||||
>
|
||||
<el-option :label="$t('common.creator')" value="create_user" />
|
||||
|
||||
<el-option :label="$t('common.name')" value="name" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-if="search_type === 'name'"
|
||||
v-model="search_form.name"
|
||||
@change="getList"
|
||||
:placeholder="$t('common.searchBar.placeholder')"
|
||||
style="width: 220px"
|
||||
clearable
|
||||
/>
|
||||
<el-select
|
||||
v-else-if="search_type === 'create_user'"
|
||||
v-model="search_form.create_user"
|
||||
@change="getList"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option v-for="u in user_options" :key="u.id" :value="u.id" :label="u.username" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button type="primary" class="ml-8">
|
||||
{{ $t('common.create') }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="create-dropdown">
|
||||
<el-dropdown-item @click="openCreateDialog(CreateKnowledgeDialog)">
|
||||
<div class="flex">
|
||||
<el-avatar class="avatar-blue mt-4" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_document.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<div class="pre-wrap ml-8">
|
||||
<div class="lighter">
|
||||
{{ $t('views.knowledge.knowledgeType.generalKnowledge') }}
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>{{ $t('views.knowledge.knowledgeType.generalInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="openCreateDialog(CreateWebKnowledgeDialog)">
|
||||
<div class="flex">
|
||||
<el-avatar class="avatar-purple mt-4" shape="square" :size="32">
|
||||
<img src="@/assets/knowledge/icon_web.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<div class="pre-wrap ml-8">
|
||||
<div class="lighter">
|
||||
{{ $t('views.knowledge.knowledgeType.webKnowledge') }}
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>{{ $t('views.knowledge.knowledgeType.webInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<div class="flex">
|
||||
<el-avatar
|
||||
class="avatar-purple mt-4"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img src="@/assets/knowledge/logo_lark.svg" alt="" />
|
||||
</el-avatar>
|
||||
<div class="pre-wrap ml-8">
|
||||
<div class="lighter">
|
||||
{{ $t('views.knowledge.knowledgeType.larkKnowledge') }}
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>{{ $t('views.knowledge.knowledgeType.larkInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<div class="flex">
|
||||
<el-avatar
|
||||
class="avatar-purple mt-4"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img src="@/assets/knowledge/logo_yuque.svg" alt="" />
|
||||
</el-avatar>
|
||||
<div class="pre-wrap ml-8">
|
||||
<div class="lighter">
|
||||
{{ $t('views.knowledge.knowledgeType.yuqueKnowledge') }}
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>{{ $t('views.knowledge.knowledgeType.yuqueInfo') }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<div v-loading.fullscreen.lock="paginationConfig.current_page === 1 && loading">
|
||||
<InfiniteScroll
|
||||
:size="knowledgeList.length"
|
||||
:total="paginationConfig.total"
|
||||
:page_size="paginationConfig.page_size"
|
||||
v-model:current_page="paginationConfig.current_page"
|
||||
@load="getList"
|
||||
:loading="loading"
|
||||
>
|
||||
<el-row v-if="knowledgeList.length > 0" :gutter="15">
|
||||
<template v-for="(item, index) in knowledgeList" :key="index">
|
||||
<el-col
|
||||
v-if="item.resource_type === 'folder'"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="12"
|
||||
:lg="8"
|
||||
:xl="6"
|
||||
class="mb-16"
|
||||
>
|
||||
<CardBox
|
||||
:title="item.name"
|
||||
:description="item.desc || $t('common.noData')"
|
||||
class="cursor"
|
||||
>
|
||||
<template #icon>
|
||||
<el-avatar shape="square" :size="32" style="background: none">
|
||||
<AppIcon iconName="app-folder" style="font-size: 32px"></AppIcon>
|
||||
</el-avatar>
|
||||
</template>
|
||||
<template #subTitle>
|
||||
<el-text class="color-secondary lighter" size="small">
|
||||
{{ $t('common.creator') }}: {{ item.username }}
|
||||
</el-text>
|
||||
</template>
|
||||
</CardBox>
|
||||
</el-col>
|
||||
<el-col v-else :xs="24" :sm="12" :md="12" :lg="8" :xl="6" class="mb-16">
|
||||
<CardBox
|
||||
:title="item.name"
|
||||
:description="item.desc"
|
||||
isShared
|
||||
class="cursor"
|
||||
@click="
|
||||
router.push({
|
||||
path: `/knowledge/system/${item.id}/documentShared`,
|
||||
query: {
|
||||
from: 'shared',
|
||||
},
|
||||
})
|
||||
"
|
||||
>
|
||||
<template #icon>
|
||||
<KnowledgeIcon :type="item.type" />
|
||||
</template>
|
||||
<template #subTitle>
|
||||
<el-text class="color-secondary" size="small">
|
||||
{{ $t('common.creator') }}: {{ item.username }}
|
||||
</el-text>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<div>
|
||||
<span class="bold mr-4">{{ item?.document_count || 0 }}</span>
|
||||
<span class="color-secondary">{{
|
||||
$t('views.knowledge.document_count')
|
||||
}}</span>
|
||||
<el-divider direction="vertical" />
|
||||
<span class="bold mr-4">{{ numberFormat(item?.char_length) || 0 }}</span>
|
||||
<span class="color-secondary">{{ $t('common.character') }}</span>
|
||||
<el-divider direction="vertical" />
|
||||
<span class="bold mr-4">{{ item?.application_mapping_count || 0 }}</span>
|
||||
<span class="color-secondary">{{
|
||||
$t('views.knowledge.relatedApp_count')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #mouseEnter>
|
||||
<div @click.stop>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button text @click.stop>
|
||||
<el-icon>
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
icon="Refresh"
|
||||
@click.stop="syncKnowledge(item)"
|
||||
v-if="item.type === 1"
|
||||
>{{ $t('views.knowledge.setting.sync') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.stop="reEmbeddingKnowledge(item)">
|
||||
<AppIcon iconName="app-vectorization"></AppIcon>
|
||||
{{ $t('views.knowledge.setting.vectorization') }}
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item
|
||||
icon="Connection"
|
||||
@click.stop="openGenerateDialog(item)"
|
||||
>{{ $t('views.document.generateQuestion.title') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
icon="Lock"
|
||||
@click.stop="openAuthorizedWorkspaceDialog(item)"
|
||||
>{{ $t('views.system.authorized_workspace') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
icon="Setting"
|
||||
@click.stop="
|
||||
router.push({
|
||||
path: `/knowledge/system/${item.id}/settingShared`,
|
||||
query: {
|
||||
from: 'shared',
|
||||
},
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ $t('common.setting') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click.stop="exportKnowledge(item)">
|
||||
<AppIcon iconName="app-export"></AppIcon
|
||||
>{{ $t('views.document.setting.export') }} Excel</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click.stop="exportZipKnowledge(item)">
|
||||
<AppIcon iconName="app-export"></AppIcon
|
||||
>{{ $t('views.document.setting.export') }} ZIP</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item icon="Delete" @click.stop="deleteKnowledge(item)">{{
|
||||
$t('common.delete')
|
||||
}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</CardBox>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
<el-empty :description="$t('common.noData')" v-else />
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
</ContentContainer>
|
||||
|
||||
<component :is="currentCreateDialog" ref="CreateKnowledgeDialogRef" />
|
||||
<CreateFolderDialog ref="CreateFolderDialogRef" @refresh="refreshFolder" />
|
||||
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" />
|
||||
<AuthorizedWorkspace ref="AuthorizedWorkspaceDialogRef"></AuthorizedWorkspace>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, reactive, shallowRef, nextTick } from 'vue'
|
||||
import KnowledgeIcon from '@/views/knowledge-shared-system/component/KnowledgeIcon.vue'
|
||||
import CreateKnowledgeDialog from './create-component/CreateKnowledgeDialog.vue'
|
||||
import CreateWebKnowledgeDialog from './create-component/CreateWebKnowledgeDialog.vue'
|
||||
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
|
||||
import GenerateRelatedDialog from '@/components/generate-related-dialog/index.vue'
|
||||
import KnowledgeApi from '@/api/shared/knowledge'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import { numberFormat } from '@/utils/common'
|
||||
import iconMap from '@/components/app-icon/icons/common'
|
||||
import { t } from '@/locales'
|
||||
import { useRouter } from 'vue-router'
|
||||
import AuthorizedWorkspace from '@/views/knowledge-shared-system/AuthorizedWorkspace.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const { folder } = useStore()
|
||||
|
||||
const loading = ref(false)
|
||||
const rightOutlined = iconMap['right-outlined'].iconReader()
|
||||
|
||||
const search_type = ref('name')
|
||||
const search_form = ref<any>({
|
||||
name: '',
|
||||
create_user: '',
|
||||
})
|
||||
|
||||
const user_options = ref<any[]>([])
|
||||
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 30,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const folderList = ref<any[]>([])
|
||||
const knowledgeList = ref<any[]>([])
|
||||
const currentFolder = ref<any>({})
|
||||
|
||||
const CreateKnowledgeDialogRef = ref()
|
||||
const currentCreateDialog = shallowRef<any>(null)
|
||||
const AuthorizedWorkspaceDialogRef = ref()
|
||||
function openCreateDialog(data: any) {
|
||||
currentCreateDialog.value = data
|
||||
nextTick(() => {
|
||||
CreateKnowledgeDialogRef.value.open(currentFolder.value)
|
||||
})
|
||||
|
||||
// common.asyncGetValid(ValidType.Dataset, ValidCount.Dataset, loading).then(async (res: any) => {
|
||||
// if (res?.data) {
|
||||
// CreateDatasetDialogRef.value.open()
|
||||
// } else if (res?.code === 400) {
|
||||
// MsgConfirm(t('common.tip'), t('views.knowledge.tip.professionalMessage'), {
|
||||
// cancelButtonText: t('common.confirm'),
|
||||
// confirmButtonText: t('common.professional'),
|
||||
// })
|
||||
// .then(() => {
|
||||
// window.open('https://maxkb.cn/pricing.html', '_blank')
|
||||
// })
|
||||
// .catch(() => {})
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
function reEmbeddingKnowledge(row: any) {
|
||||
KnowledgeApi.putReEmbeddingKnowledge(row.id).then(() => {
|
||||
MsgSuccess(t('common.submitSuccess'))
|
||||
})
|
||||
}
|
||||
|
||||
const SyncWebDialogRef = ref()
|
||||
|
||||
function syncKnowledge(row: any) {
|
||||
SyncWebDialogRef.value.open(row.id)
|
||||
}
|
||||
|
||||
const search_type_change = () => {
|
||||
search_form.value = { name: '', create_user: '' }
|
||||
}
|
||||
|
||||
function getList() {
|
||||
const params = {
|
||||
folder_id: currentFolder.value?.id || 'root',
|
||||
[search_type.value]: search_form.value[search_type.value],
|
||||
}
|
||||
|
||||
KnowledgeApi.getKnowledgeList(paginationConfig, params, loading).then((res) => {
|
||||
paginationConfig.total = res.data.total
|
||||
knowledgeList.value = [...knowledgeList.value, ...res.data.records]
|
||||
})
|
||||
}
|
||||
|
||||
function getFolder() {
|
||||
const params = {}
|
||||
folder.asyncGetFolder('KNOWLEDGE', params, loading).then((res: any) => {
|
||||
folderList.value = res.data
|
||||
currentFolder.value = res.data?.[0] || {}
|
||||
getList()
|
||||
})
|
||||
}
|
||||
|
||||
function folderClickHandel(row: any) {
|
||||
currentFolder.value = row
|
||||
knowledgeList.value = []
|
||||
getList()
|
||||
}
|
||||
|
||||
const CreateFolderDialogRef = ref()
|
||||
|
||||
function openCreateFolder() {
|
||||
CreateFolderDialogRef.value.open('KNOWLEDGE', currentFolder.value.parent_id)
|
||||
}
|
||||
|
||||
const GenerateRelatedDialogRef = ref<InstanceType<typeof GenerateRelatedDialog>>()
|
||||
function openGenerateDialog(row: any) {
|
||||
if (GenerateRelatedDialogRef.value) {
|
||||
GenerateRelatedDialogRef.value.open([], 'knowledge', row.id)
|
||||
}
|
||||
}
|
||||
|
||||
function openAuthorizedWorkspaceDialog(row: any) {
|
||||
if (AuthorizedWorkspaceDialogRef.value) {
|
||||
AuthorizedWorkspaceDialogRef.value.open(row)
|
||||
}
|
||||
}
|
||||
|
||||
const exportKnowledge = (item: any) => {
|
||||
KnowledgeApi.exportKnowledge(item.name, item.id, loading).then((ok) => {
|
||||
MsgSuccess(t('common.exportSuccess'))
|
||||
})
|
||||
}
|
||||
const exportZipKnowledge = (item: any) => {
|
||||
KnowledgeApi.exportZipKnowledge(item.name, item.id, loading).then((ok) => {
|
||||
MsgSuccess(t('common.exportSuccess'))
|
||||
})
|
||||
}
|
||||
|
||||
function deleteKnowledge(row: any) {
|
||||
MsgConfirm(
|
||||
`${t('views.knowledge.delete.confirmTitle')}${row.name} ?`,
|
||||
`${t('views.knowledge.delete.confirmMessage1')} ${row.application_mapping_count} ${t('views.knowledge.delete.confirmMessage2')}`,
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
KnowledgeApi.delKnowledge(row.id, loading).then(() => {
|
||||
const index = knowledgeList.value.findIndex((v) => v.id === row.id)
|
||||
knowledgeList.value.splice(index, 1)
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function refreshFolder() {
|
||||
getFolder()
|
||||
getList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.knowledge-shared {
|
||||
padding-left: 8px;
|
||||
.shared-header {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:deep(.el-icon i) {
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
color: #1f2329;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -7,9 +7,11 @@
|
|||
:currentNodeKey="currentFolder?.id"
|
||||
@handleNodeClick="folderClickHandel"
|
||||
class="p-8"
|
||||
isShared
|
||||
/>
|
||||
</template>
|
||||
<ContentContainer :header="currentFolder?.name">
|
||||
<SharedWorkspace v-if="currentFolder.id === 'share'"></SharedWorkspace>
|
||||
<ContentContainer v-else :header="currentFolder?.name">
|
||||
<template #search>
|
||||
<div class="flex">
|
||||
<div class="flex-between complex-search">
|
||||
|
|
@ -176,6 +178,7 @@
|
|||
<CardBox
|
||||
:title="item.name"
|
||||
:description="item.desc"
|
||||
:isShared="currentFolder.id === 'share'"
|
||||
class="cursor"
|
||||
@click="
|
||||
router.push({ path: `/knowledge/${item.id}/${currentFolder.id}/document` })
|
||||
|
|
@ -282,6 +285,7 @@ import CreateWebKnowledgeDialog from './create-component/CreateWebKnowledgeDialo
|
|||
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
|
||||
import GenerateRelatedDialog from '@/components/generate-related-dialog/index.vue'
|
||||
import KnowledgeApi from '@/api/knowledge/knowledge'
|
||||
import SharedWorkspace from '@/views/knowledge-shared-system/SharedWorkspace.vue'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import useStore from '@/stores'
|
||||
import { numberFormat } from '@/utils/common'
|
||||
|
|
@ -357,6 +361,7 @@ function getList() {
|
|||
folder_id: currentFolder.value?.id || localStorage.getItem('workspace_id'),
|
||||
[search_type.value]: search_form.value[search_type.value],
|
||||
}
|
||||
|
||||
KnowledgeApi.getKnowledgeList(paginationConfig, params, loading).then((res) => {
|
||||
paginationConfig.total = res.data.total
|
||||
knowledgeList.value = [...knowledgeList.value, ...res.data.records]
|
||||
|
|
@ -375,6 +380,7 @@ function getFolder() {
|
|||
function folderClickHandel(row: any) {
|
||||
currentFolder.value = row
|
||||
knowledgeList.value = []
|
||||
if (currentFolder.value.id === 'share') return
|
||||
getList()
|
||||
}
|
||||
|
||||
|
|
@ -408,8 +414,8 @@ function deleteKnowledge(row: any) {
|
|||
`${t('views.knowledge.delete.confirmMessage1')} ${row.application_mapping_count} ${t('views.knowledge.delete.confirmMessage2')}`,
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger'
|
||||
}
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
KnowledgeApi.delKnowledge(row.id, loading).then(() => {
|
||||
|
|
@ -421,7 +427,6 @@ function deleteKnowledge(row: any) {
|
|||
.catch(() => {})
|
||||
}
|
||||
|
||||
|
||||
function refreshFolder() {
|
||||
getFolder()
|
||||
getList()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
v-model="drawer"
|
||||
:direction="direction"
|
||||
size="600"
|
||||
:destroy-on-close="true"
|
||||
:before-close="cancelClick"
|
||||
>
|
||||
<template #header>
|
||||
<h4>
|
||||
{{
|
||||
isEdit
|
||||
? $t('common.param.editParam')
|
||||
: $t('common.param.addParam')
|
||||
}}
|
||||
</h4>
|
||||
</template>
|
||||
<template #default>
|
||||
<DynamicsFormConstructor
|
||||
v-model="currentItem"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
ref="DynamicsFormConstructorRef"
|
||||
></DynamicsFormConstructor>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div style="flex: auto">
|
||||
<el-button @click="cancelClick">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="confirmClick()">{{
|
||||
isEdit ? $t('common.save') : $t('common.add')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import type { DrawerProps } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import DynamicsFormConstructor from '@/components/dynamics-form/constructor/index.vue'
|
||||
|
||||
const drawer = ref(false)
|
||||
const direction = ref<DrawerProps['direction']>('rtl')
|
||||
const isEdit = ref(false)
|
||||
const DynamicsFormConstructorRef = ref<InstanceType<typeof DynamicsFormConstructor>>()
|
||||
|
||||
const currentItem = ref(null)
|
||||
const currentIndex = ref(null)
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const open = (row: any, index: any) => {
|
||||
if (row) {
|
||||
currentItem.value = cloneDeep(row)
|
||||
currentIndex.value = index
|
||||
isEdit.value = true
|
||||
}
|
||||
drawer.value = true
|
||||
}
|
||||
|
||||
function cancelClick() {
|
||||
drawer.value = false
|
||||
isEdit.value = false
|
||||
currentItem.value = null
|
||||
currentIndex.value = null
|
||||
}
|
||||
|
||||
function confirmClick() {
|
||||
const formEl = DynamicsFormConstructorRef.value
|
||||
formEl?.validate().then((valid) => {
|
||||
if (valid) {
|
||||
emit('refresh', formEl?.getData(), currentIndex.value)
|
||||
drawer.value = false
|
||||
isEdit.value = false
|
||||
currentItem.value = null
|
||||
currentIndex.value = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -0,0 +1,494 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:before-close="close"
|
||||
append-to-body
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<el-breadcrumb separator=">">
|
||||
<el-breadcrumb-item>
|
||||
<span @click="toSelectProvider" class="select-provider">
|
||||
{{ $t('views.model.providerPlaceholder') }}
|
||||
</span>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item
|
||||
><span class="active-breadcrumb">{{
|
||||
`${$t('common.add')} ${providerValue?.name}`
|
||||
}}</span></el-breadcrumb-item
|
||||
>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="$t('views.model.modelForm.title.baseInfo')" name="base-info">
|
||||
<DynamicsForm
|
||||
v-model="form_data"
|
||||
:render_data="model_form_field"
|
||||
:model="form_data"
|
||||
ref="dynamicsFormRef"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
class="mb-24"
|
||||
label-width="auto"
|
||||
>
|
||||
<template #default>
|
||||
<el-form-item prop="name" :rules="base_form_data_rule.name">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="mr-4">
|
||||
<span> {{ $t('views.model.modelForm.modeName.label') }} </span>
|
||||
</div>
|
||||
<el-tooltip effect="dark" placement="right">
|
||||
<template #content>
|
||||
<p>{{ $t('views.model.modelForm.modeName.tooltip') }}</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="base_form_data.name"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
:placeholder="$t('views.model.modelForm.modeName.placeholder')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="permission_type" :rules="base_form_data_rule.permission_type">
|
||||
<template #label>
|
||||
<span>{{ $t('views.model.modelForm.permissionType.label') }}</span>
|
||||
</template>
|
||||
<el-radio-group v-model="base_form_data.permission_type" class="card__radio">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(value, key) of PermissionType" :key="key">
|
||||
<el-col :span="12">
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-16"
|
||||
:class="base_form_data.permission_type === key ? 'active' : ''"
|
||||
>
|
||||
<el-radio :value="key" size="large">
|
||||
<p class="mb-4">{{ $t(value) }}</p>
|
||||
<el-text type="info">
|
||||
{{ $t(PermissionDesc[key]) }}
|
||||
</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="model_type" :rules="base_form_data_rule.model_type">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<span class="mr-4">{{ $t('views.model.modelForm.model_type.label') }} </span>
|
||||
<el-tooltip effect="dark" placement="right">
|
||||
<template #content>
|
||||
<p>{{ $t('views.model.modelForm.model_type.tooltip1') }}</p>
|
||||
<p>{{ $t('views.model.modelForm.model_type.tooltip2') }}</p>
|
||||
<p>{{ $t('views.model.modelForm.model_type.tooltip3') }}</p>
|
||||
<p>{{ $t('views.model.modelForm.model_type.tooltip4') }}</p>
|
||||
<p>{{ $t('views.model.modelForm.model_type.tooltip5') }}</p>
|
||||
<p>{{ $t('views.model.modelForm.model_type.tooltip6') }}</p>
|
||||
<p>{{ $t('views.model.modelForm.model_type.tooltip7') }}</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-select
|
||||
v-loading="model_type_loading"
|
||||
@change="list_base_model($event, true)"
|
||||
v-model="base_form_data.model_type"
|
||||
class="w-full m-2"
|
||||
:placeholder="$t('views.model.modelForm.model_type.placeholder')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in model_type_list"
|
||||
:key="item.value"
|
||||
:label="item.key"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="model_name" :rules="base_form_data_rule.model_name">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="mr-4">
|
||||
<span>{{ $t('views.model.modelForm.base_model.label') }} </span>
|
||||
<span class="danger ml-4">{{
|
||||
$t('views.model.modelForm.base_model.tooltip')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-select
|
||||
@change="getModelForm($event)"
|
||||
v-loading="base_model_loading"
|
||||
v-model="base_form_data.model_name"
|
||||
class="w-full m-2"
|
||||
:placeholder="$t('views.model.modelForm.base_model.placeholder')"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option v-for="item in base_model_list" :key="item.name" :value="item.name">
|
||||
<template #default>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="flex-between mr-4">
|
||||
<span>{{ item.name }} </span>
|
||||
</div>
|
||||
<el-tooltip effect="dark" placement="right" v-if="item.desc">
|
||||
<template #content>
|
||||
<p class="w-280">{{ item.desc }}</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</DynamicsForm>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('views.model.modelForm.title.advancedInfo')" name="advanced-info">
|
||||
<el-empty
|
||||
v-if="!base_form_data.model_type || !base_form_data.model_name"
|
||||
:description="$t('views.model.tip.emptyMessage1')"
|
||||
/>
|
||||
<el-empty
|
||||
v-else-if="
|
||||
base_form_data.model_type === 'RERANKER' ||
|
||||
base_form_data.model_type === 'EMBEDDING' ||
|
||||
base_form_data.model_type === 'STT'
|
||||
"
|
||||
:description="$t('views.model.tip.emptyMessage2')"
|
||||
/>
|
||||
<div class="flex-between mb-8" v-else>
|
||||
<h5>{{ $t('views.model.modelForm.title.modelParams') }}</h5>
|
||||
<el-button
|
||||
type="text"
|
||||
@click.stop="openAddDrawer()"
|
||||
:disabled="
|
||||
base_form_data.model_type !== 'TTS' &&
|
||||
base_form_data.model_type !== 'LLM' &&
|
||||
base_form_data.model_type !== 'IMAGE' &&
|
||||
base_form_data.model_type !== 'TTI'
|
||||
"
|
||||
>
|
||||
<AppIcon iconName="Plus" class="add-icon" />{{ $t('common.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
:data="base_form_data.model_params_form"
|
||||
v-if="base_form_data.model_params_form?.length > 0"
|
||||
class="mb-16"
|
||||
>
|
||||
<el-table-column
|
||||
prop="label"
|
||||
:label="$t('dynamicsForm.paramForm.name.label')"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.label && row.label.input_type === 'TooltipLabel'">{{
|
||||
row.label.label
|
||||
}}</span>
|
||||
<span v-else>{{ row.label }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="field"
|
||||
:label="$t('dynamicsForm.paramForm.field.label')"
|
||||
show-overflow-tooltip
|
||||
width="95px"
|
||||
/>
|
||||
<el-table-column :label="$t('dynamicsForm.paramForm.input_type.label')" width="110px">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info" class="info-tag">{{
|
||||
input_type_list.find((item) => item.value === row.input_type)?.label
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="default_value"
|
||||
:label="$t('dynamicsForm.default.label')"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column :label="$t('common.required')">
|
||||
<template #default="{ row }">
|
||||
<div @click.stop>
|
||||
<el-switch disabled size="small" v-model="row.required" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('common.operation')" align="left" width="90">
|
||||
<template #default="{ row, $index }">
|
||||
<span class="mr-4">
|
||||
<el-tooltip effect="dark" :content="$t('common.modify')" placement="top">
|
||||
<el-button type="primary" text @click.stop="openAddDrawer(row, $index)">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip effect="dark" :content="$t('common.delete')" placement="top">
|
||||
<el-button type="primary" text @click="deleteParam($index)">
|
||||
<el-icon>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="close">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit" :loading="loading">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<AddParamDrawer ref="AddParamRef" @refresh="refresh" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import type { Provider, BaseModel } from '@/api/type/model'
|
||||
import type { Dict, KeyValue } from '@/api/type/common'
|
||||
import ModelApi from '@/api/shared/model'
|
||||
import ProviderApi from '@/api/shared/provider'
|
||||
import type { FormField } from '@/components/dynamics-form/type'
|
||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||
import type { FormRules } from 'element-plus'
|
||||
import { MsgError, MsgSuccess, MsgWarning } from '@/utils/message'
|
||||
import { PermissionType, PermissionDesc } from '@/enums/model'
|
||||
import { input_type_list } from '@/components/dynamics-form/constructor/data'
|
||||
import AddParamDrawer from '@/views/model-shared-system/component/AddParamDrawer.vue'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const providerValue = ref<Provider>()
|
||||
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
|
||||
const emit = defineEmits(['change', 'submit'])
|
||||
const loading = ref<boolean>(false)
|
||||
const model_type_loading = ref<boolean>(false)
|
||||
const base_model_loading = ref<boolean>(false)
|
||||
const model_type_list = ref<Array<KeyValue<string, string>>>([])
|
||||
|
||||
const base_model_list = ref<Array<BaseModel>>()
|
||||
const model_form_field = ref<Array<FormField>>([])
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const activeName = ref('base-info')
|
||||
const AddParamRef = ref()
|
||||
|
||||
const base_form_data_rule = ref<FormRules>({
|
||||
name: {
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
message: t('views.model.modelForm.modeName.requiredMessage'),
|
||||
},
|
||||
permission_type: {
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
message: t('views.model.modelForm.permissionType.requiredMessage'),
|
||||
},
|
||||
model_type: {
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
message: t('views.model.modelForm.model_type.requiredMessage'),
|
||||
},
|
||||
model_name: {
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
message: t('views.model.modelForm.base_model.requiredMessage'),
|
||||
},
|
||||
})
|
||||
|
||||
const base_form_data = ref<{
|
||||
name: string
|
||||
permission_type: string
|
||||
model_type: string
|
||||
model_name: string
|
||||
model_params_form: any
|
||||
}>({ name: '', model_type: '', model_name: '', permission_type: 'PRIVATE', model_params_form: [] })
|
||||
|
||||
const credential_form_data = ref<Dict<any>>({})
|
||||
|
||||
const form_data = computed({
|
||||
get: () => {
|
||||
return {
|
||||
...credential_form_data.value,
|
||||
name: base_form_data.value.name,
|
||||
model_type: base_form_data.value.model_type,
|
||||
model_name: base_form_data.value.model_name,
|
||||
permission_type: base_form_data.value.permission_type,
|
||||
model_params_form: base_form_data.value.model_params_form,
|
||||
}
|
||||
},
|
||||
set: (event: any) => {
|
||||
credential_form_data.value = event
|
||||
},
|
||||
})
|
||||
|
||||
const getModelForm = (model_name: string) => {
|
||||
if (!form_data.value.model_type) {
|
||||
MsgWarning(t('views.model.modelForm.model_type.requiredMessage'))
|
||||
base_form_data.value.model_name = ''
|
||||
return
|
||||
}
|
||||
if (providerValue.value) {
|
||||
ProviderApi.getModelCreateForm(
|
||||
providerValue.value.provider,
|
||||
form_data.value.model_type,
|
||||
model_name,
|
||||
).then((ok) => {
|
||||
model_form_field.value = ok.data
|
||||
// 渲染动态表单
|
||||
dynamicsFormRef.value?.render(model_form_field.value, undefined)
|
||||
})
|
||||
|
||||
ProviderApi.listBaseModelParamsForm(
|
||||
providerValue.value.provider,
|
||||
form_data.value.model_type,
|
||||
model_name,
|
||||
base_model_loading,
|
||||
).then((ok) => {
|
||||
base_form_data.value.model_params_form = ok.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const open = (provider: Provider, model_type?: string) => {
|
||||
ProviderApi.listModelType(provider.provider, model_type_loading).then((ok) => {
|
||||
model_type_list.value = ok.data
|
||||
})
|
||||
providerValue.value = provider
|
||||
dialogVisible.value = true
|
||||
base_form_data.value.model_type = model_type || ''
|
||||
activeName.value = 'base-info'
|
||||
if (model_type) {
|
||||
list_base_model(model_type)
|
||||
}
|
||||
}
|
||||
|
||||
const list_base_model = (model_type: any, change?: boolean) => {
|
||||
if (change) {
|
||||
base_form_data.value.model_name = ''
|
||||
base_form_data.value.model_params_form = []
|
||||
}
|
||||
if (providerValue.value) {
|
||||
ProviderApi.listBaseModel(providerValue.value.provider, model_type, base_model_loading).then(
|
||||
(ok) => {
|
||||
base_model_list.value = ok.data
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
base_form_data.value = {
|
||||
name: '',
|
||||
model_type: '',
|
||||
model_name: '',
|
||||
permission_type: 'PRIVATE',
|
||||
model_params_form: [],
|
||||
}
|
||||
credential_form_data.value = {}
|
||||
model_form_field.value = []
|
||||
base_model_list.value = []
|
||||
loading.value = false
|
||||
dialogVisible.value = false
|
||||
}
|
||||
const submit = () => {
|
||||
dynamicsFormRef.value
|
||||
?.validate()
|
||||
.then(() => {
|
||||
if (providerValue.value) {
|
||||
ModelApi.createModel(
|
||||
{
|
||||
...base_form_data.value,
|
||||
credential: credential_form_data.value,
|
||||
provider: providerValue.value.provider,
|
||||
},
|
||||
loading,
|
||||
).then((ok) => {
|
||||
close()
|
||||
MsgSuccess(t('views.model.tip.createSuccessMessage'))
|
||||
emit('submit')
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
MsgError(t('views.model.tip.createErrorMessage'))
|
||||
})
|
||||
}
|
||||
|
||||
function openAddDrawer(data?: any, index?: any) {
|
||||
AddParamRef.value?.open(data, index)
|
||||
}
|
||||
|
||||
function deleteParam(index: any) {
|
||||
base_form_data.value.model_params_form.splice(index, 1)
|
||||
}
|
||||
|
||||
function refresh(data: any, index: any) {
|
||||
for (let i = 0; i < base_form_data.value.model_params_form.length; i++) {
|
||||
const field = base_form_data.value.model_params_form[i].field
|
||||
let label = base_form_data.value.model_params_form[i].label
|
||||
if (label && label.input_type === 'TooltipLabel') {
|
||||
label = label.label
|
||||
}
|
||||
let label2 = data.label
|
||||
if (label2 && label2.input_type === 'TooltipLabel') {
|
||||
label2 = label2.label
|
||||
}
|
||||
|
||||
if (field === data.field && index !== i) {
|
||||
MsgError(t('views.model.tip.errorMessage') + data.field)
|
||||
return
|
||||
}
|
||||
if (label === label2 && index !== i) {
|
||||
MsgError(t('views.model.tip.errorMessage') + label)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (index !== null) {
|
||||
base_form_data.value.model_params_form.splice(index, 1, data)
|
||||
} else {
|
||||
base_form_data.value.model_params_form.push(data)
|
||||
}
|
||||
}
|
||||
|
||||
const toSelectProvider = () => {
|
||||
close()
|
||||
emit('change')
|
||||
}
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.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>
|
||||
|
|
@ -0,0 +1,308 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:before-close="close"
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<el-breadcrumb separator=">">
|
||||
<el-breadcrumb-item
|
||||
><span class="active-breadcrumb">{{
|
||||
`${$t('common.edit')} ${providerValue?.name}`
|
||||
}}</span></el-breadcrumb-item
|
||||
>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<DynamicsForm
|
||||
v-loading="formLoading"
|
||||
v-model="form_data"
|
||||
:render_data="model_form_field"
|
||||
:model="form_data"
|
||||
ref="dynamicsFormRef"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
<template #default>
|
||||
<el-form-item prop="name" :rules="base_form_data_rule.name">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="mr-4">
|
||||
<span>{{ $t('views.model.modelForm.modeName.label') }} </span>
|
||||
</div>
|
||||
<el-tooltip effect="dark" placement="right">
|
||||
<template #content>
|
||||
<p>{{ $t('views.model.modelForm.modeName.tooltip') }}</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="base_form_data.name"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
:placeholder="$t('views.model.modelForm.modeName.placeholder')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="permission_type" :rules="base_form_data_rule.permission_type">
|
||||
<template #label>
|
||||
<span>{{ $t('views.model.modelForm.permissionType.label') }}</span>
|
||||
</template>
|
||||
|
||||
<el-radio-group v-model="base_form_data.permission_type" class="card__radio">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(value, key) of PermissionType" :key="key">
|
||||
<el-col :span="12">
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-16"
|
||||
:class="base_form_data.permission_type === key ? 'active' : ''"
|
||||
>
|
||||
<el-radio :value="key" size="large">
|
||||
<p class="mb-4">{{ $t(value) }}</p>
|
||||
<el-text type="info">
|
||||
{{ $t(PermissionDesc[key]) }}
|
||||
</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="model_type" :rules="base_form_data_rule.model_type">
|
||||
<template #label>
|
||||
<span>{{ $t('views.model.modelForm.model_type.label') }}</span>
|
||||
</template>
|
||||
<el-select
|
||||
disabled
|
||||
v-loading="model_type_loading"
|
||||
@change="list_base_model($event, true)"
|
||||
v-model="base_form_data.model_type"
|
||||
class="w-full m-2"
|
||||
:placeholder="$t('views.model.modelForm.model_type.placeholder')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in model_type_list"
|
||||
:key="item.value"
|
||||
:label="item.key"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="model_name" :rules="base_form_data_rule.model_name">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="mr-4">
|
||||
<span>{{ $t('views.model.modelForm.base_model.label') }} </span>
|
||||
<span class="danger ml-4">{{
|
||||
$t('views.model.modelForm.base_model.tooltip')
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-select
|
||||
@change="getModelForm($event)"
|
||||
v-loading="base_model_loading"
|
||||
v-model="base_form_data.model_name"
|
||||
class="w-full m-2"
|
||||
:placeholder="$t('views.model.modelForm.base_model.requiredMessage')"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option v-for="item in base_model_list" :key="item.name" :value="item.name">
|
||||
<template #default>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="flex-between mr-4">
|
||||
<span>{{ item.name }} </span>
|
||||
</div>
|
||||
<el-tooltip effect="dark" placement="right" v-if="item.desc">
|
||||
<template #content>
|
||||
<p>{{ item.desc }}</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</DynamicsForm>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="close">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit" :loading="loading">
|
||||
{{ $t('common.modify') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {ref, computed} from 'vue'
|
||||
import type {Provider, BaseModel, Model} from '@/api/type/model'
|
||||
import type {Dict, KeyValue} from '@/api/type/common'
|
||||
import ModelApi from '@/api/shared/model'
|
||||
import ProviderApi from '@/api/shared/provider'
|
||||
import type {FormField} from '@/components/dynamics-form/type'
|
||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||
import type {FormRules} from 'element-plus'
|
||||
import {MsgSuccess} from '@/utils/message'
|
||||
import {PermissionType, PermissionDesc} from '@/enums/model'
|
||||
import {t} from '@/locales'
|
||||
|
||||
const providerValue = ref<Provider>()
|
||||
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
|
||||
const emit = defineEmits(['change', 'submit'])
|
||||
const loading = ref<boolean>(false)
|
||||
const formLoading = ref<boolean>(false)
|
||||
const model_type_loading = ref<boolean>(false)
|
||||
const base_model_loading = ref<boolean>(false)
|
||||
const model_type_list = ref<Array<KeyValue<string, string>>>([])
|
||||
const modelValue = ref<Model>()
|
||||
const base_model_list = ref<Array<BaseModel>>([])
|
||||
const model_form_field = ref<Array<FormField>>([])
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
const base_form_data_rule = ref<FormRules>({
|
||||
name: {
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
message: t('views.model.modelForm.modeName.requiredMessage'),
|
||||
},
|
||||
model_type: {
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
message: t('views.model.modelForm.model_type.requiredMessage'),
|
||||
},
|
||||
model_name: {
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
message: t('views.model.modelForm.base_model.requiredMessage'),
|
||||
},
|
||||
})
|
||||
|
||||
const base_form_data = ref<{
|
||||
name: string
|
||||
permission_type: string
|
||||
model_type: string
|
||||
|
||||
model_name: string
|
||||
}>({name: '', model_type: '', model_name: '', permission_type: 'PRIVATE'})
|
||||
|
||||
const credential_form_data = ref<Dict<any>>({})
|
||||
|
||||
const form_data = computed({
|
||||
get: () => {
|
||||
return {...credential_form_data.value, ...base_form_data.value}
|
||||
},
|
||||
set: (event: any) => {
|
||||
credential_form_data.value = event
|
||||
},
|
||||
})
|
||||
|
||||
const getModelForm = (model_name: string) => {
|
||||
if (providerValue.value) {
|
||||
ProviderApi.getModelCreateForm(
|
||||
providerValue.value.provider,
|
||||
form_data.value.model_type,
|
||||
model_name,
|
||||
).then((ok) => {
|
||||
model_form_field.value = ok.data
|
||||
if (modelValue.value) {
|
||||
// 渲染动态表单
|
||||
dynamicsFormRef.value?.render(model_form_field.value, modelValue.value.credential)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const list_base_model = (model_type: any, change?: boolean) => {
|
||||
if (change) {
|
||||
base_form_data.value.model_name = ''
|
||||
}
|
||||
if (providerValue.value) {
|
||||
ProviderApi.listBaseModel(providerValue.value.provider, model_type, base_model_loading).then(
|
||||
(ok) => {
|
||||
base_model_list.value = ok.data
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
const open = (provider: Provider, model: Model) => {
|
||||
modelValue.value = model
|
||||
ModelApi.getModelById(model.id, formLoading).then((ok) => {
|
||||
modelValue.value = ok.data
|
||||
ProviderApi.listModelType(model.provider, model_type_loading).then((ok) => {
|
||||
model_type_list.value = ok.data
|
||||
list_base_model(model.model_type)
|
||||
})
|
||||
providerValue.value = provider
|
||||
|
||||
base_form_data.value = {
|
||||
name: model.name,
|
||||
permission_type: model.permission_type,
|
||||
model_type: model.model_type,
|
||||
model_name: model.model_name,
|
||||
}
|
||||
form_data.value = model.credential
|
||||
getModelForm(model.model_name)
|
||||
})
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
base_form_data.value = {name: '', model_type: '', model_name: '', permission_type: ''}
|
||||
dynamicsFormRef.value?.ruleFormRef?.resetFields()
|
||||
credential_form_data.value = {}
|
||||
model_form_field.value = []
|
||||
base_model_list.value = []
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const submit = () => {
|
||||
dynamicsFormRef.value?.validate().then(() => {
|
||||
if (modelValue.value) {
|
||||
ModelApi.updateModel(
|
||||
modelValue.value.id,
|
||||
{
|
||||
...base_form_data.value,
|
||||
credential: credential_form_data.value,
|
||||
},
|
||||
loading,
|
||||
).then((ok) => {
|
||||
MsgSuccess(t('views.model.tip.updateSuccessMessage'))
|
||||
close()
|
||||
emit('submit')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({open, close})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.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>
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
<template>
|
||||
<card-box isShared :title="model.name" shadow="hover" class="model-card">
|
||||
<template #icon>
|
||||
<span style="height: 32px; width: 32px" :innerHTML="icon"></span>
|
||||
</template>
|
||||
<template #title>
|
||||
<div class="flex" style="height: 22px">
|
||||
{{ model.name }}
|
||||
<span v-if="currentModel.status === 'ERROR'">
|
||||
<el-tooltip effect="dark" :content="errMessage" placement="top">
|
||||
<el-icon class="danger ml-4" size="18"><Warning /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span v-if="currentModel.status === 'PAUSE_DOWNLOAD'">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="`${$t('views.model.modelForm.base_model.label')}: ${props.model.model_name} ${$t('views.model.tip.downloadError')}`"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon class="danger ml-4" size="18"><Warning /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #subTitle>
|
||||
<el-text class="color-secondary lighter" size="small">
|
||||
{{ $t('common.creator') }}: {{ model.username }}
|
||||
</el-text>
|
||||
</template>
|
||||
<ul>
|
||||
<li class="flex mb-4">
|
||||
<el-text type="info" class="color-secondary"
|
||||
>{{ $t('views.model.modelForm.model_type.label') }}
|
||||
</el-text>
|
||||
<span class="ellipsis ml-16">
|
||||
{{ $t(modelType[model.model_type as keyof typeof modelType]) }}</span
|
||||
>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<el-text type="info" class="color-secondary"
|
||||
>{{ $t('views.model.modelForm.base_model.label') }}
|
||||
</el-text>
|
||||
<span class="ellipsis-1 ml-16" style="height: 20px; width: 70%">
|
||||
{{ model.model_name }}</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- progress -->
|
||||
<div class="progress-mask" v-if="currentModel.status === 'DOWNLOAD'">
|
||||
<!-- <DownloadLoading class="percentage" /> -->
|
||||
|
||||
<div class="percentage-label flex-center">
|
||||
{{ $t('views.model.download.downloading') }} <span class="dotting"></span>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
class="ml-16"
|
||||
:disabled="!is_permisstion"
|
||||
@click.stop="cancelDownload"
|
||||
>{{ $t('views.model.download.cancelDownload') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #mouseEnter>
|
||||
<el-dropdown trigger="click">
|
||||
<el-button text @click.stop>
|
||||
<el-icon>
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item icon="Lock" @click.stop="openAuthorizedWorkspaceDialog(model)">{{
|
||||
$t('views.system.authorized_workspace')
|
||||
}}</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
icon="EditPen"
|
||||
:disabled="!is_permisstion"
|
||||
text
|
||||
@click.stop="openEditModel"
|
||||
>
|
||||
{{ $t('common.modify') }}
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item
|
||||
v-if="
|
||||
currentModel.model_type === 'TTS' ||
|
||||
currentModel.model_type === 'LLM' ||
|
||||
currentModel.model_type === 'IMAGE' ||
|
||||
currentModel.model_type === 'TTI'
|
||||
"
|
||||
:disabled="!is_permisstion"
|
||||
icon="Setting"
|
||||
@click.stop="openParamSetting"
|
||||
>
|
||||
{{ $t('views.model.modelForm.title.paramSetting') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
divided
|
||||
icon="Delete"
|
||||
:disabled="!is_permisstion"
|
||||
text
|
||||
@click.stop="deleteModel"
|
||||
>
|
||||
{{ $t('common.delete') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<EditModel ref="editModelRef" @submit="emit('change')"></EditModel>
|
||||
<ParamSettingDialog ref="paramSettingRef" :model="model" />
|
||||
<AuthorizedWorkspace ref="AuthorizedWorkspaceDialogRef"></AuthorizedWorkspace>
|
||||
</card-box>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { Provider, Model } from '@/api/type/model'
|
||||
import ModelApi from '@/api/shared/model'
|
||||
import { computed, ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import EditModel from '@/views/model-shared-system/component/EditModel.vue'
|
||||
// import DownloadLoading from '@/components/loading/DownloadLoading.vue'
|
||||
import { MsgConfirm } from '@/utils/message'
|
||||
import { modelType } from '@/enums/model'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import AuthorizedWorkspace from '@/views/knowledge-shared-system/AuthorizedWorkspace.vue'
|
||||
import ParamSettingDialog from './ParamSettingDialog.vue'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const props = defineProps<{
|
||||
model: Model
|
||||
provider_list: Array<Provider>
|
||||
updateModelById: (model_id: string, model: Model) => void
|
||||
}>()
|
||||
|
||||
const { user } = useStore()
|
||||
const downModel = ref<Model>()
|
||||
|
||||
const is_permisstion = computed(() => {
|
||||
return user.userInfo?.id == props.model.user_id
|
||||
})
|
||||
const currentModel = computed(() => {
|
||||
if (downModel.value) {
|
||||
return downModel.value
|
||||
} else {
|
||||
return props.model
|
||||
}
|
||||
})
|
||||
const AuthorizedWorkspaceDialogRef = ref()
|
||||
|
||||
const errMessage = computed(() => {
|
||||
if (currentModel.value.meta && currentModel.value.meta.message) {
|
||||
if (currentModel.value.meta.message === 'pull model manifest: file does not exist') {
|
||||
return `${currentModel.value.model_name} ${t('views.model.tip.noModel')}`
|
||||
}
|
||||
return currentModel.value.meta.message
|
||||
}
|
||||
return ''
|
||||
})
|
||||
const emit = defineEmits(['change', 'update:model'])
|
||||
const editModelRef = ref<InstanceType<typeof EditModel>>()
|
||||
let interval: any
|
||||
const deleteModel = () => {
|
||||
MsgConfirm(
|
||||
t('views.model.delete.confirmTitle'),
|
||||
`${t('views.model.delete.confirmMessage')}${props.model.name} ?`,
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
ModelApi.deleteModel(props.model.id).then(() => {
|
||||
emit('change')
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
function openAuthorizedWorkspaceDialog(row: any) {
|
||||
if (AuthorizedWorkspaceDialogRef.value) {
|
||||
AuthorizedWorkspaceDialogRef.value.open(row, 'Model')
|
||||
}
|
||||
}
|
||||
const cancelDownload = () => {
|
||||
ModelApi.pauseDownload(props.model.id).then(() => {
|
||||
downModel.value = undefined
|
||||
emit('change')
|
||||
})
|
||||
}
|
||||
const openEditModel = () => {
|
||||
const provider = props.provider_list.find((p) => p.provider === props.model.provider)
|
||||
if (provider) {
|
||||
editModelRef.value?.open(provider, props.model)
|
||||
}
|
||||
}
|
||||
const icon = computed(() => {
|
||||
return props.provider_list.find((p) => p.provider === props.model.provider)?.icon
|
||||
})
|
||||
|
||||
/**
|
||||
* 初始化轮询
|
||||
*/
|
||||
const initInterval = () => {
|
||||
interval = setInterval(() => {
|
||||
if (currentModel.value.status === 'DOWNLOAD') {
|
||||
ModelApi.getModelMetaById(props.model.id).then((ok) => {
|
||||
downModel.value = ok.data
|
||||
})
|
||||
} else {
|
||||
if (downModel.value) {
|
||||
props.updateModelById(props.model.id, downModel.value)
|
||||
downModel.value = undefined
|
||||
}
|
||||
}
|
||||
}, 6000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭轮询
|
||||
*/
|
||||
const closeInterval = () => {
|
||||
if (interval) {
|
||||
clearInterval(interval)
|
||||
}
|
||||
}
|
||||
|
||||
const paramSettingRef = ref<InstanceType<typeof ParamSettingDialog>>()
|
||||
const openParamSetting = () => {
|
||||
paramSettingRef.value?.open()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initInterval()
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
// 清除定时任务
|
||||
closeInterval()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.model-card {
|
||||
min-height: 135px;
|
||||
min-width: auto;
|
||||
|
||||
.operation-button {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
bottom: 12px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.progress-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 99;
|
||||
text-align: center;
|
||||
|
||||
.percentage {
|
||||
margin-top: 55px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
// .percentage-value {
|
||||
// display: flex;
|
||||
// font-size: 13px;
|
||||
// align-items: center;
|
||||
// color: var(--app-text-color-secondary);
|
||||
// }
|
||||
.percentage-label {
|
||||
margin-top: 50px;
|
||||
margin-left: 10px;
|
||||
font-size: 13px;
|
||||
color: var(--app-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.model.modelForm.title.paramSetting')"
|
||||
v-model="dialogVisible"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:before-close="close"
|
||||
>
|
||||
<el-button type="primary" @click="openAddDrawer()" class="mb-12">
|
||||
{{ $t('views.model.modelForm.title.addParam') }}
|
||||
</el-button>
|
||||
<el-table :data="modelParamsForm" class="mb-16">
|
||||
<el-table-column
|
||||
prop="label"
|
||||
:label="$t('dynamicsForm.paramForm.name.label')"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.label && row.label.input_type === 'TooltipLabel'">{{
|
||||
row.label.label
|
||||
}}</span>
|
||||
<span v-else>{{ row.label }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="field"
|
||||
:label="$t('dynamicsForm.paramForm.field.label')"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column :label="$t('dynamicsForm.paramForm.input_type.label')" width="110px">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info" class="info-tag">{{
|
||||
input_type_list.find((item) => item.value === row.input_type)?.label
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="default_value"
|
||||
:label="$t('dynamicsForm.default.label')"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column :label="$t('common.required')">
|
||||
<template #default="{ row }">
|
||||
<div @click.stop>
|
||||
<el-switch disabled size="small" v-model="row.required" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('common.operation')" align="left" width="90">
|
||||
<template #default="{ row, $index }">
|
||||
<span class="mr-4">
|
||||
<el-tooltip effect="dark" :content="$t('common.modify')" placement="top">
|
||||
<el-button type="primary" text @click.stop="openAddDrawer(row, $index)">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip effect="dark" :content="$t('common.delete')" placement="top">
|
||||
<el-button type="primary" text @click="deleteParam($index)">
|
||||
<el-icon>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="close">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit" :loading="loading">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<AddParamDrawer ref="AddParamRef" @refresh="refresh" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Model } from '@/api/type/model'
|
||||
import { ref } from 'vue'
|
||||
import AddParamDrawer from './AddParamDrawer.vue'
|
||||
import { MsgError, MsgSuccess } from '@/utils/message'
|
||||
import ModelApi from '@/api/shared/model'
|
||||
import { input_type_list } from '@/components/dynamics-form/constructor/data'
|
||||
import { t } from '@/locales'
|
||||
const props = defineProps<{
|
||||
model: Model
|
||||
}>()
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const modelParamsForm = ref<any[]>([])
|
||||
const AddParamRef = ref()
|
||||
|
||||
const open = () => {
|
||||
dialogVisible.value = true
|
||||
loading.value = true
|
||||
ModelApi.getModelParamsForm(props.model.id, loading)
|
||||
.then((ok) => {
|
||||
loading.value = false
|
||||
modelParamsForm.value = ok.data
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
function openAddDrawer(data?: any, index?: any) {
|
||||
AddParamRef.value?.open(data, index)
|
||||
}
|
||||
|
||||
function deleteParam(index: any) {
|
||||
modelParamsForm.value.splice(index, 1)
|
||||
}
|
||||
|
||||
function refresh(data: any, index: any) {
|
||||
for (let i = 0; i < modelParamsForm.value.length; i++) {
|
||||
const field = modelParamsForm.value[i].field
|
||||
let label = modelParamsForm.value[i].label
|
||||
if (label && label.input_type === 'TooltipLabel') {
|
||||
label = label.label
|
||||
}
|
||||
let label2 = data.label
|
||||
if (label2 && label2.input_type === 'TooltipLabel') {
|
||||
label2 = label2.label
|
||||
}
|
||||
|
||||
if (field === data.field && index !== i) {
|
||||
MsgError(t('views.model.tip.errorMessage') + data.field)
|
||||
return
|
||||
}
|
||||
if (label === label2 && index !== i) {
|
||||
MsgError(t('views.model.tip.errorMessage') + label)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (index !== null) {
|
||||
modelParamsForm.value.splice(index, 1, data)
|
||||
} else {
|
||||
modelParamsForm.value.push(data)
|
||||
}
|
||||
}
|
||||
|
||||
function submit() {
|
||||
ModelApi.updateModelParamsForm(props.model.id, modelParamsForm.value, loading).then(
|
||||
(ok) => {
|
||||
MsgSuccess(t('views.model.tip.saveSuccessMessage'))
|
||||
close()
|
||||
// emit('submit')
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
<template>
|
||||
<div class="provider-list">
|
||||
<el-scrollbar>
|
||||
<div class="p-8">
|
||||
<div
|
||||
class="all-mode flex cursor"
|
||||
@click="clickListHandle(allObj as Provider)"
|
||||
:class="!active?.provider ? 'all-mode-active color-primary-1' : ''"
|
||||
>
|
||||
<AppIcon
|
||||
class="mr-8 color-primary"
|
||||
style="height: 20px; width: 20px"
|
||||
:iconName="'app-all-menu-active'"
|
||||
></AppIcon>
|
||||
<span>{{ $t('views.model.modelType.allModel') }}</span>
|
||||
</div>
|
||||
|
||||
<el-collapse class="model-collapse" expand-icon-position="left">
|
||||
<el-collapse-item
|
||||
:title="$t('views.model.modelType.publicModel')"
|
||||
name="1"
|
||||
icon="CaretRight"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex align-center">
|
||||
<AppIcon iconName="app-folder" style="font-size: 20px"></AppIcon>
|
||||
<span class="ml-8">
|
||||
{{ $t('views.model.modelType.publicModel') }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<common-list
|
||||
:data="online_provider_list"
|
||||
v-loading="loading"
|
||||
@click="clickListHandle"
|
||||
value-key="provider"
|
||||
default-active=""
|
||||
ref="commonList1"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="flex align-center">
|
||||
<span
|
||||
:innerHTML="row.icon"
|
||||
alt=""
|
||||
style="height: 20px; width: 20px"
|
||||
class="mr-8"
|
||||
/>
|
||||
<span class="ellipsis-1" :title="row.name">{{ row.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</common-list>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
:title="$t('views.model.modelType.privateModel')"
|
||||
name="2"
|
||||
icon="CaretRight"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex align-center">
|
||||
<AppIcon iconName="app-folder" style="font-size: 20px"></AppIcon>
|
||||
<span class="ml-8">
|
||||
{{ $t('views.model.modelType.privateModel') }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<common-list
|
||||
:data="local_provider_list"
|
||||
v-loading="loading"
|
||||
@click="clickListHandle"
|
||||
value-key="provider"
|
||||
default-active=""
|
||||
ref="commonList2"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="flex align-center">
|
||||
<span
|
||||
:innerHTML="row.icon"
|
||||
alt=""
|
||||
style="height: 20px; width: 20px"
|
||||
class="mr-8"
|
||||
/>
|
||||
<span class="ellipsis-1" :title="row.name">{{ row.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</common-list>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { watch, ref } from 'vue'
|
||||
import type { Provider, Model } from '@/api/type/model'
|
||||
import { modelTypeList, allObj } from '@/views/model-shared-system/component/data'
|
||||
const props = defineProps<{
|
||||
data: Array<Provider>
|
||||
loading: boolean
|
||||
active?: Provider
|
||||
}>()
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
const online_provider_list = ref<Array<Provider>>([])
|
||||
const local_provider_list = ref<Array<Provider>>([])
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(list) => {
|
||||
const local_provider = [
|
||||
'model_ollama_provider',
|
||||
'model_local_provider',
|
||||
'model_xinference_provider',
|
||||
'model_vllm_provider',
|
||||
]
|
||||
list
|
||||
.filter((v) => v.provider)
|
||||
?.forEach((item) => {
|
||||
if (local_provider.indexOf(item.provider) > -1) {
|
||||
local_provider_list.value.push(item)
|
||||
} else {
|
||||
online_provider_list.value.push(item)
|
||||
}
|
||||
})
|
||||
online_provider_list.value.sort((a, b) => a.provider.localeCompare(b.provider))
|
||||
local_provider_list.value.sort((a, b) => a.provider.localeCompare(b.provider))
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const clickListHandle = (item: Provider) => {
|
||||
emit('click', item)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.provider-list {
|
||||
height: calc(var(--app-main-height));
|
||||
.all-mode {
|
||||
padding: 10px 8px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.all-mode-active {
|
||||
border-radius: 4px;
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
.model-collapse {
|
||||
border-top: none !important;
|
||||
border-bottom: none !important;
|
||||
:deep(.el-collapse-item__header) {
|
||||
border-bottom: none !important;
|
||||
padding-left: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
height: 40px;
|
||||
background: none;
|
||||
&:hover {
|
||||
background: var(--app-text-color-light-1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
:deep(.el-collapse-item) {
|
||||
margin-top: 2px;
|
||||
}
|
||||
:deep(.common-list) {
|
||||
li {
|
||||
padding-left: 50px !important;
|
||||
}
|
||||
}
|
||||
:deep(.el-collapse-item__wrap) {
|
||||
border-bottom: none !important;
|
||||
background: none !important;
|
||||
}
|
||||
:deep(.el-collapse-item__content) {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:before-close="close"
|
||||
append-to-body
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex-between">
|
||||
<h4>{{ $t('views.model.providerPlaceholder') }}</h4>
|
||||
<el-dropdown>
|
||||
<span class="cursor">
|
||||
{{ currentModelType || $t('views..model.modelType.allModel') }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
v-for="item in modelTypeOptions"
|
||||
:key="item.value"
|
||||
@click="checkModelType(item.value)"
|
||||
>
|
||||
<span>{{ item.text }}</span>
|
||||
<el-icon v-if="currentModelType === item.text"><Check /></el-icon>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<el-row :gutter="12" v-loading="loading">
|
||||
<el-col :span="12" class="mb-16" v-for="(data, index) in list_provider" :key="index">
|
||||
<el-card shadow="hover" @click="go_create(data)">
|
||||
<div class="flex align-center cursor">
|
||||
<span :innerHTML="data.icon" alt="" style="height: 24px; width: 24px" class="mr-8" />
|
||||
<span>{{ data.name }}</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import ProviderApi from '@/api/shared/provider'
|
||||
import type { Provider } from '@/api/type/model'
|
||||
import { modelTypeList } from './data'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const list_provider = ref<Array<Provider>>([])
|
||||
const currentModelType = ref('')
|
||||
const selectModelType = ref('')
|
||||
const modelTypeOptions = [{ text: t('views.model.modelType.allModel'), value: '' }, ...modelTypeList]
|
||||
|
||||
const open = (model_type?: string) => {
|
||||
dialogVisible.value = true
|
||||
const option = modelTypeOptions.find((item) => item.text === currentModelType.value)
|
||||
checkModelType(model_type ? model_type : option ? option.value : '')
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const checkModelType = (model_type: string) => {
|
||||
selectModelType.value = model_type
|
||||
currentModelType.value = modelTypeOptions.filter((item) => item.value === model_type)[0].text
|
||||
ProviderApi.getProviderByModelType(model_type, loading).then((ok) => {
|
||||
list_provider.value = ok.data
|
||||
list_provider.value.sort((a, b) => a.provider.localeCompare(b.provider))
|
||||
})
|
||||
}
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
const go_create = (provider: Provider) => {
|
||||
close()
|
||||
emit('change', provider, selectModelType.value)
|
||||
}
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { modelType } from '@/enums/model'
|
||||
import { t } from '@/locales'
|
||||
export const modelTypeList = [
|
||||
{ text: t(modelType['LLM']), value: 'LLM' },
|
||||
{ text: t(modelType['EMBEDDING']), value: 'EMBEDDING' },
|
||||
{ text: t(modelType['RERANKER']), value: 'RERANKER' },
|
||||
{ text: t(modelType['STT']), value: 'STT' },
|
||||
{ text: t(modelType['TTS']), value: 'TTS' },
|
||||
{ text: t(modelType['IMAGE']), value: 'IMAGE' },
|
||||
{ text: t(modelType['TTI']), value: 'TTI' }
|
||||
]
|
||||
|
||||
|
||||
export const allObj = {
|
||||
icon: '',
|
||||
provider: '',
|
||||
name: t('views.model.modelType.allModel'),
|
||||
}
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
<template>
|
||||
<div class="model-shared">
|
||||
<div class="shared-header">
|
||||
<span class="title">{{ t('views.system.shared_resources') }}</span>
|
||||
<el-icon size="12">
|
||||
<rightOutlined></rightOutlined>
|
||||
</el-icon>
|
||||
<span class="sub-title">{{ t('views.model.title') }}</span>
|
||||
</div>
|
||||
<LayoutContainer>
|
||||
<template #left>
|
||||
<h4 class="p-16 mb-8 pb-0">{{ $t('views.model.provider') }}</h4>
|
||||
<ProviderComponent
|
||||
:data="provider_list"
|
||||
@click="clickListHandle"
|
||||
:loading="loading"
|
||||
:active="active_provider"
|
||||
/>
|
||||
</template>
|
||||
<ContentContainer
|
||||
:header="t('views.model.modelType.allModel')"
|
||||
v-loading="list_model_loading"
|
||||
>
|
||||
<template #search>
|
||||
<div class="flex">
|
||||
<div class="flex-between complex-search">
|
||||
<el-select
|
||||
class="complex-search__left"
|
||||
v-model="search_type"
|
||||
style="width: 120px"
|
||||
@change="search_type_change"
|
||||
>
|
||||
<el-option :label="$t('common.creator')" value="create_user" />
|
||||
<el-option
|
||||
:label="$t('views.model.modelForm.permissionType.label')"
|
||||
value="permission_type"
|
||||
/>
|
||||
<el-option
|
||||
:label="$t('views.model.modelForm.model_type.label')"
|
||||
value="model_type"
|
||||
/>
|
||||
<el-option :label="$t('views.model.modelForm.modeName.label')" value="name" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-if="search_type === 'name'"
|
||||
v-model="model_search_form.name"
|
||||
@change="list_model"
|
||||
:placeholder="$t('common.searchBar.placeholder')"
|
||||
style="width: 220px"
|
||||
clearable
|
||||
/>
|
||||
<el-select
|
||||
v-else-if="search_type === 'create_user'"
|
||||
v-model="model_search_form.create_user"
|
||||
@change="list_model"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option
|
||||
v-for="u in user_options"
|
||||
:key="u.id"
|
||||
:value="u.id"
|
||||
:label="u.username"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-else-if="search_type === 'permission_type'"
|
||||
v-model="model_search_form.permission_type"
|
||||
clearable
|
||||
@change="list_model"
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option :label="$t('common.public')" value="PUBLIC" />
|
||||
<el-option :label="$t('common.private')" value="PRIVATE" />
|
||||
</el-select>
|
||||
<el-select
|
||||
v-else-if="search_type === 'model_type'"
|
||||
v-model="model_search_form.model_type"
|
||||
clearable
|
||||
@change="list_model"
|
||||
style="width: 220px"
|
||||
>
|
||||
<template v-for="item in modelTypeList" :key="item.value">
|
||||
<el-option :label="item.text" :value="item.value" />
|
||||
</template>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button class="ml-16" type="primary" @click="openCreateModel(active_provider)">
|
||||
{{ $t('views.model.addModel') }}</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="model-list-height">
|
||||
<el-scrollbar>
|
||||
<el-row v-if="model_split_list.length > 0" :gutter="15" class="w-full">
|
||||
<template v-for="(row, index) in model_split_list" :key="index">
|
||||
<el-col
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="12"
|
||||
:lg="8"
|
||||
:xl="6"
|
||||
class="mb-16"
|
||||
v-for="(model, i) in row"
|
||||
:key="i"
|
||||
>
|
||||
<ModelCard
|
||||
@change="list_model"
|
||||
:updateModelById="updateModelById"
|
||||
:model="model"
|
||||
:provider_list="provider_list"
|
||||
>
|
||||
</ModelCard>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
<el-empty :description="$t('common.noData')" v-else />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</ContentContainer>
|
||||
|
||||
<CreateModelDialog
|
||||
ref="createModelRef"
|
||||
@submit="list_model"
|
||||
@change="openCreateModel($event)"
|
||||
></CreateModelDialog>
|
||||
|
||||
<SelectProviderDialog
|
||||
ref="selectProviderRef"
|
||||
@change="(provider, modelType) => openCreateModel(provider, modelType)"
|
||||
></SelectProviderDialog>
|
||||
</LayoutContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, computed } from 'vue'
|
||||
import ProviderApi from '@/api/shared/provider'
|
||||
import type { Provider, Model } from '@/api/type/model'
|
||||
import ModelCard from '@/views/model-shared-system/component/ModelCard.vue'
|
||||
import ProviderComponent from '@/views/model-shared-system/component/Provider.vue'
|
||||
import { splitArray } from '@/utils/common'
|
||||
import { modelTypeList, allObj } from '@/views/model-shared-system/component/data'
|
||||
import CreateModelDialog from '@/views/model-shared-system/component/CreateModelDialog.vue'
|
||||
import iconMap from '@/components/app-icon/icons/common'
|
||||
import SelectProviderDialog from '@/views/model-shared-system/component/SelectProviderDialog.vue'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const { model } = useStore()
|
||||
const rightOutlined = iconMap['right-outlined'].iconReader()
|
||||
|
||||
const commonList1 = ref()
|
||||
const commonList2 = ref()
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
const active_provider = ref<Provider>()
|
||||
const search_type = ref('name')
|
||||
const model_search_form = ref<{
|
||||
name: string
|
||||
create_user: string
|
||||
permission_type: string
|
||||
model_type: string
|
||||
}>({
|
||||
name: '',
|
||||
create_user: '',
|
||||
permission_type: '',
|
||||
model_type: '',
|
||||
})
|
||||
const user_options = ref<any[]>([])
|
||||
const list_model_loading = ref<boolean>(false)
|
||||
const provider_list = ref<Array<Provider>>([])
|
||||
|
||||
const model_list = ref<Array<Model>>([])
|
||||
|
||||
const updateModelById = (model_id: string, model: Model) => {
|
||||
model_list.value
|
||||
.filter((m) => m.id == model_id)
|
||||
.forEach((m) => {
|
||||
m.status = model.status
|
||||
})
|
||||
}
|
||||
const model_split_list = computed(() => {
|
||||
return splitArray(model_list.value, 2)
|
||||
})
|
||||
const createModelRef = ref<InstanceType<typeof CreateModelDialog>>()
|
||||
const selectProviderRef = ref<InstanceType<typeof SelectProviderDialog>>()
|
||||
|
||||
const clickListHandle = (item: Provider) => {
|
||||
active_provider.value = item
|
||||
list_model()
|
||||
if (active_provider.value.provider === '') {
|
||||
commonList1.value.clearCurrent()
|
||||
commonList2.value.clearCurrent()
|
||||
}
|
||||
}
|
||||
|
||||
const openCreateModel = (provider?: Provider, model_type?: string) => {
|
||||
if (provider && provider.provider) {
|
||||
createModelRef.value?.open(provider, model_type)
|
||||
} else {
|
||||
selectProviderRef.value?.open()
|
||||
}
|
||||
}
|
||||
|
||||
const list_model = () => {
|
||||
const params = active_provider.value?.provider ? { provider: active_provider.value.provider } : {}
|
||||
model
|
||||
.asyncGetModel({ ...model_search_form.value, ...params }, list_model_loading)
|
||||
.then((ok: any) => {
|
||||
model_list.value = ok.data
|
||||
const v = model_list.value.map((m) => ({ id: m.user_id, username: m.username }))
|
||||
if (user_options.value.length === 0) {
|
||||
user_options.value = Array.from(new Map(v.map((item) => [item.id, item])).values())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const search_type_change = () => {
|
||||
model_search_form.value = { name: '', create_user: '', permission_type: '', model_type: '' }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
ProviderApi.getProvider(loading).then((ok) => {
|
||||
active_provider.value = allObj
|
||||
provider_list.value = [allObj, ...ok.data]
|
||||
|
||||
list_model()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.model-shared {
|
||||
padding: 21px 14px 16px 24px;
|
||||
height: 100%;
|
||||
|
||||
.shared-header {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 21px;
|
||||
|
||||
:deep(.el-icon i) {
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
color: #1f2329;
|
||||
}
|
||||
}
|
||||
|
||||
.h-full {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 2px 4px 0px #1f23291f;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
<template>
|
||||
<el-card
|
||||
shadow="hover"
|
||||
class="paragraph-box cursor"
|
||||
@mouseenter="cardEnter()"
|
||||
@mouseleave="cardLeave()"
|
||||
>
|
||||
<h2 class="mb-16">{{ data.title || '-' }}</h2>
|
||||
<el-card
|
||||
v-show="show"
|
||||
class="paragraph-box-operation mt-8 mr-8"
|
||||
shadow="always"
|
||||
style="--el-card-padding: 8px 12px; --el-card-border-radius: 8px"
|
||||
>
|
||||
<el-switch
|
||||
:loading="changeStateloading"
|
||||
v-model="data.is_active"
|
||||
:before-change="() => changeState(data)"
|
||||
size="small"
|
||||
/>
|
||||
|
||||
<el-divider direction="vertical" />
|
||||
<span class="mr-8">
|
||||
<el-button link @click="editParagraph(data)">
|
||||
<el-icon :size="16" :title="$t('views.applicationWorkflow.control.zoomOut')">
|
||||
<EditPen />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</span>
|
||||
<span class="mr-8">
|
||||
<el-button link>
|
||||
<el-icon :size="16" :title="$t('views.applicationWorkflow.control.zoomOut')">
|
||||
<el-icon><CirclePlus /></el-icon>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</span>
|
||||
<el-dropdown trigger="click" :teleported="false">
|
||||
<el-button text>
|
||||
<el-icon><MoreFilled /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openGenerateDialog(data)">
|
||||
<el-icon><Connection /></el-icon>
|
||||
{{ $t('views.document.generateQuestion.title') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="openSelectDocumentDialog(data)">
|
||||
<AppIcon iconName="app-migrate"></AppIcon>
|
||||
{{ $t('views.document.setting.migration') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item icon="Delete" @click.stop="deleteParagraph(data)">{{
|
||||
$t('common.delete')
|
||||
}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-card>
|
||||
<MdPreview
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="data.content"
|
||||
class="maxkb-md"
|
||||
/>
|
||||
|
||||
<ParagraphDialog ref="ParagraphDialogRef" :title="title" @refresh="refresh" />
|
||||
<SelectDocumentDialog ref="SelectDocumentDialogRef" @refresh="refreshMigrateParagraph" />
|
||||
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="refresh" />
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, useSlots } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { t } from '@/locales'
|
||||
import useStore from '@/stores'
|
||||
import GenerateRelatedDialog from '@/components/generate-related-dialog/index.vue'
|
||||
import ParagraphDialog from '@/views/paragraph/component/ParagraphDialog.vue'
|
||||
import SelectDocumentDialog from '@/views/paragraph/component/SelectDocumentDialog.vue'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
|
||||
const { paragraph } = useStore()
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id, documentId },
|
||||
} = route as any
|
||||
const props = defineProps<{
|
||||
data: any
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['changeState', 'deleteParagraph'])
|
||||
const loading = ref(false)
|
||||
const changeStateloading = ref(false)
|
||||
const show = ref(false)
|
||||
// card上面存在dropdown菜单
|
||||
const subHovered = ref(false)
|
||||
function cardEnter() {
|
||||
show.value = true
|
||||
subHovered.value = false
|
||||
}
|
||||
|
||||
function cardLeave() {
|
||||
show.value = subHovered.value
|
||||
}
|
||||
|
||||
function changeState(row: any) {
|
||||
const obj = {
|
||||
is_active: !row.is_active,
|
||||
}
|
||||
paragraph
|
||||
.asyncPutParagraph(id, documentId, row.id, obj, changeStateloading)
|
||||
.then((res) => {
|
||||
emit('changeState', row.id)
|
||||
return true
|
||||
})
|
||||
.catch(() => {
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
const GenerateRelatedDialogRef = ref<InstanceType<typeof GenerateRelatedDialog>>()
|
||||
function openGenerateDialog(row: any) {
|
||||
if (GenerateRelatedDialogRef.value) {
|
||||
GenerateRelatedDialogRef.value.open([], 'knowledge', 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 || '-'} ?`,
|
||||
t('views.paragraph.delete.confirmMessage'),
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
paragraph.asyncDelParagraph(id, documentId, row.id, loading).then(() => {
|
||||
emit('deleteParagraph', row.id)
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const SelectDocumentDialogRef = ref()
|
||||
const ParagraphDialogRef = ref()
|
||||
const title = ref('')
|
||||
function editParagraph(row: any) {
|
||||
title.value = t('views.paragraph.paragraphDetail')
|
||||
ParagraphDialogRef.value.open(row)
|
||||
}
|
||||
|
||||
function refresh() {}
|
||||
|
||||
function refreshMigrateParagraph() {}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.paragraph-box {
|
||||
background: var(--app-layout-bg-color);
|
||||
border: 1px solid #ffffff;
|
||||
box-shadow: none !important;
|
||||
position: relative;
|
||||
&:hover {
|
||||
background: rgba(31, 35, 41, 0.1);
|
||||
border: 1px solid #dee0e3;
|
||||
}
|
||||
.paragraph-box-operation {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border: 1px solid #dee0e3;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="dialogVisible"
|
||||
width="80%"
|
||||
class="paragraph-dialog"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<el-row v-loading="loading">
|
||||
<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; ">
|
||||
<el-button text @click="isEdit = true" v-if="problemId && !isEdit">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<ParagraphForm ref="paragraphFormRef" :data="detail" :isEdit="isEdit" />
|
||||
</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 type="primary" :disabled="loading" @click="handleDebounceClick">
|
||||
{{$t('common.save')}}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6" class="border-l" style="width: 300px">
|
||||
<!-- 关联问题 -->
|
||||
<ProblemComponent
|
||||
:problemId="problemId"
|
||||
:docId="document_id"
|
||||
:knowledgeId="dataset_id"
|
||||
ref="ProblemRef"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer v-if="!problemId">
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{$t('common.cancel')}} </el-button>
|
||||
<el-button :disabled="loading" type="primary" @click="handleDebounceClick">
|
||||
{{$t('common.submit')}}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, nextTick } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { cloneDeep, debounce } from 'lodash'
|
||||
import ParagraphForm from '@/views/paragraph/component/ParagraphForm.vue'
|
||||
import ProblemComponent from '@/views/paragraph/component/ProblemComponent.vue'
|
||||
import paragraphApi from '@/api/shared/paragraph'
|
||||
import useStore from '@/stores'
|
||||
|
||||
const props = defineProps({
|
||||
title: String
|
||||
})
|
||||
|
||||
const { paragraph } = useStore()
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id, documentId }
|
||||
} = route as any
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const ProblemRef = ref()
|
||||
const paragraphFormRef = ref<any>()
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
const loading = ref(false)
|
||||
const problemId = ref('')
|
||||
const detail = ref<any>({})
|
||||
const isEdit = ref(false)
|
||||
const document_id = ref('')
|
||||
const dataset_id = ref('')
|
||||
const cloneData = ref(null)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
problemId.value = ''
|
||||
detail.value = {}
|
||||
isEdit.value = false
|
||||
document_id.value = ''
|
||||
dataset_id.value = ''
|
||||
cloneData.value = null
|
||||
}
|
||||
})
|
||||
|
||||
const cancelEdit = () => {
|
||||
isEdit.value = false
|
||||
detail.value = cloneDeep(cloneData.value)
|
||||
}
|
||||
|
||||
const open = (data: any) => {
|
||||
if (data) {
|
||||
detail.value.title = data.title
|
||||
detail.value.content = data.content
|
||||
cloneData.value = cloneDeep(detail.value)
|
||||
problemId.value = data.id
|
||||
document_id.value = data.document_id
|
||||
dataset_id.value = data.dataset_id || id
|
||||
} else {
|
||||
isEdit.value = true
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const submitHandle = async () => {
|
||||
if (await paragraphFormRef.value?.validate()) {
|
||||
loading.value = true
|
||||
if (problemId.value) {
|
||||
paragraph
|
||||
.asyncPutParagraph(
|
||||
dataset_id.value,
|
||||
documentId || document_id.value,
|
||||
problemId.value,
|
||||
paragraphFormRef.value?.form,
|
||||
loading
|
||||
)
|
||||
.then((res: any) => {
|
||||
isEdit.value = false
|
||||
emit('refresh', res.data)
|
||||
})
|
||||
} else {
|
||||
const obj =
|
||||
ProblemRef.value.problemList.length > 0
|
||||
? {
|
||||
problem_list: ProblemRef.value.problemList,
|
||||
...paragraphFormRef.value?.form
|
||||
}
|
||||
: paragraphFormRef.value?.form
|
||||
paragraphApi.postParagraph(id, documentId, obj, loading).then((res) => {
|
||||
dialogVisible.value = false
|
||||
emit('refresh')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleDebounceClick = debounce(() => {
|
||||
submitHandle()
|
||||
}, 200)
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
<template>
|
||||
<el-form
|
||||
ref="paragraphFormRef"
|
||||
:model="form"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:rules="rules"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item :label="$t('views.paragraph.form.paragraphTitle.label')">
|
||||
<el-input
|
||||
v-if="isEdit"
|
||||
v-model="form.title"
|
||||
:placeholder="$t('views.paragraph.form.paragraphTitle.placeholder')"
|
||||
maxlength="256"
|
||||
show-word-limit
|
||||
>
|
||||
</el-input>
|
||||
<span class="lighter" v-else>{{ form.title || '-' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.paragraph.form.content.label')" prop="content">
|
||||
<MdEditor
|
||||
v-if="isEdit"
|
||||
v-model="form.content"
|
||||
:placeholder="$t('views.paragraph.form.content.placeholder')"
|
||||
:maxLength="100000"
|
||||
:preview="false"
|
||||
:toolbars="toolbars"
|
||||
style="height: 300px"
|
||||
@onUploadImg="onUploadImg"
|
||||
:footers="footers"
|
||||
>
|
||||
<template #defFooters>
|
||||
<span style="margin-left: -6px">/ 100000</span>
|
||||
</template>
|
||||
</MdEditor>
|
||||
<MdPreview
|
||||
v-else
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="form.content"
|
||||
class="maxkb-md"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onUnmounted, watch } from 'vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import imageApi from '@/api/image'
|
||||
import { t } from '@/locales'
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
isEdit: Boolean
|
||||
})
|
||||
|
||||
const toolbars = [
|
||||
'bold',
|
||||
'underline',
|
||||
'italic',
|
||||
'-',
|
||||
'title',
|
||||
'strikeThrough',
|
||||
'sub',
|
||||
'sup',
|
||||
'quote',
|
||||
'unorderedList',
|
||||
'orderedList',
|
||||
'task',
|
||||
'-',
|
||||
'codeRow',
|
||||
'code',
|
||||
'link',
|
||||
'image',
|
||||
'table',
|
||||
'mermaid',
|
||||
'katex',
|
||||
'-',
|
||||
'revoke',
|
||||
'next',
|
||||
'=',
|
||||
'pageFullscreen',
|
||||
'preview',
|
||||
'htmlPreview'
|
||||
] as any[]
|
||||
|
||||
const footers = ['markdownTotal', 0, '=', 1, 'scrollSwitch']
|
||||
|
||||
const editorRef = ref()
|
||||
|
||||
const form = ref<any>({
|
||||
title: '',
|
||||
content: ''
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
content: [
|
||||
{ required: true, message: t('views.paragraph.form.content.requiredMessage1'), trigger: 'blur' },
|
||||
{ max: 100000, message: t('views.paragraph.form.content.requiredMessage2'), trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
|
||||
const paragraphFormRef = ref<FormInstance>()
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(value) => {
|
||||
if (value && JSON.stringify(value) !== '{}') {
|
||||
form.value.title = value.title
|
||||
form.value.content = value.content
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => props.isEdit,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
paragraphFormRef.value?.clearValidate()
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
/*
|
||||
表单校验
|
||||
*/
|
||||
function validate() {
|
||||
if (!paragraphFormRef.value) return
|
||||
return paragraphFormRef.value.validate((valid: any) => {
|
||||
return valid
|
||||
})
|
||||
}
|
||||
|
||||
const onUploadImg = async (files: any, callback: any) => {
|
||||
const res = await Promise.all(
|
||||
files.map((file: any) => {
|
||||
return new Promise((rev, rej) => {
|
||||
const fd = new FormData()
|
||||
fd.append('file', file)
|
||||
|
||||
imageApi
|
||||
.postImage(fd)
|
||||
.then((res: any) => {
|
||||
rev(res)
|
||||
})
|
||||
.catch((error) => rej(error))
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
callback(res.map((item) => item.data))
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
form.value = {
|
||||
title: '',
|
||||
content: ''
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
form
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
<template>
|
||||
<p class="bold title p-24" style="padding-bottom: 0">
|
||||
<span class="flex align-center">
|
||||
<span>{{ $t('views.paragraph.relatedProblem.title') }}</span>
|
||||
<el-divider direction="vertical" class="mr-4" />
|
||||
<el-button text @click="addProblem">
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-button>
|
||||
</span>
|
||||
</p>
|
||||
<div v-loading="loading">
|
||||
<el-scrollbar height="500px">
|
||||
<div class="p-24" style="padding-top: 16px">
|
||||
<el-select
|
||||
v-if="isAddProblem"
|
||||
v-model="problemValue"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
:reserve-keyword="false"
|
||||
:placeholder="$t('views.paragraph.relatedProblem.placeholder')"
|
||||
remote
|
||||
:remote-method="remoteMethod"
|
||||
:loading="optionLoading"
|
||||
@change="addProblemHandle"
|
||||
@blur="isAddProblem = false"
|
||||
class="mb-16"
|
||||
popper-class="select-popper"
|
||||
:popper-append-to-body="false"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in problemOptions"
|
||||
:key="item.id"
|
||||
:label="item.content"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.content }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
<template v-for="(item, index) in problemList" :key="index">
|
||||
<TagEllipsis
|
||||
@close="delProblemHandle(item, index)"
|
||||
class="question-tag"
|
||||
type="info"
|
||||
effect="plain"
|
||||
closable
|
||||
>
|
||||
<auto-tooltip :content="item.content">
|
||||
{{ item.content }}
|
||||
</auto-tooltip>
|
||||
</TagEllipsis>
|
||||
</template>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import paragraphApi from '@/api/shared/paragraph'
|
||||
import useStore from '@/stores'
|
||||
|
||||
const props = defineProps({
|
||||
problemId: String,
|
||||
docId: String,
|
||||
knowledgeId: String,
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id, documentId }, // id为knowledgeId
|
||||
} = route as any
|
||||
|
||||
const { problem } = useStore()
|
||||
const inputRef = ref()
|
||||
const loading = ref(false)
|
||||
const isAddProblem = ref(false)
|
||||
|
||||
const problemValue = ref('')
|
||||
const problemList = ref<any[]>([])
|
||||
|
||||
const problemOptions = ref<any[]>([])
|
||||
const optionLoading = ref(false)
|
||||
|
||||
watch(
|
||||
() => props.problemId,
|
||||
(value) => {
|
||||
if (value) {
|
||||
getProblemList()
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
)
|
||||
|
||||
function delProblemHandle(item: any, index: number) {
|
||||
if (item.id) {
|
||||
problem
|
||||
.asyncDisassociationProblem(
|
||||
props.knowledgeId || id,
|
||||
documentId || props.docId,
|
||||
props.problemId || '',
|
||||
item.id,
|
||||
loading,
|
||||
)
|
||||
.then((res: any) => {
|
||||
getProblemList()
|
||||
})
|
||||
} else {
|
||||
problemList.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function getProblemList() {
|
||||
loading.value = true
|
||||
paragraphApi
|
||||
.getParagraphProblem(props.knowledgeId || id, documentId || props.docId, props.problemId || '')
|
||||
.then((res) => {
|
||||
problemList.value = res.data
|
||||
loading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function addProblem() {
|
||||
isAddProblem.value = true
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus()
|
||||
})
|
||||
}
|
||||
function addProblemHandle(val: string) {
|
||||
if (props.problemId) {
|
||||
const api = problemOptions.value.some((option) => option.id === val)
|
||||
? problem.asyncAssociationProblem(
|
||||
props.knowledgeId || id,
|
||||
documentId || props.docId,
|
||||
props.problemId,
|
||||
val,
|
||||
loading,
|
||||
)
|
||||
: paragraphApi.postParagraphProblem(
|
||||
props.knowledgeId || id,
|
||||
documentId || props.docId,
|
||||
props.problemId,
|
||||
{
|
||||
content: val,
|
||||
},
|
||||
loading,
|
||||
)
|
||||
api.then(() => {
|
||||
getProblemList()
|
||||
problemValue.value = ''
|
||||
isAddProblem.value = false
|
||||
})
|
||||
} else {
|
||||
const problem = problemOptions.value.find((option) => option.id === val)
|
||||
const content = problem ? problem.content : val
|
||||
if (!problemList.value.some((item) => item.content === content)) {
|
||||
problemList.value.push({ content: content })
|
||||
}
|
||||
|
||||
problemValue.value = ''
|
||||
isAddProblem.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const remoteMethod = (query: string) => {
|
||||
getProblemOption(query)
|
||||
}
|
||||
|
||||
function getProblemOption(filterText?: string) {
|
||||
return problem
|
||||
.asyncGetProblem(
|
||||
props.knowledgeId || (id as string),
|
||||
{ current_page: 1, page_size: 100 },
|
||||
filterText && { content: filterText },
|
||||
optionLoading,
|
||||
)
|
||||
.then((res: any) => {
|
||||
problemOptions.value = res.data.records
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getProblemOption()
|
||||
})
|
||||
onUnmounted(() => {
|
||||
problemList.value = []
|
||||
problemValue.value = ''
|
||||
isAddProblem.value = false
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
problemList,
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="`${$t('views.log.selectDataset')}/${$t('common.fileUpload.document')}`"
|
||||
v-model="dialogVisible"
|
||||
width="500"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:rules="rules"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item :label="$t('views.log.selectDataset')" prop="dataset_id">
|
||||
<el-select
|
||||
v-model="form.dataset_id"
|
||||
filterable
|
||||
:placeholder="$t('views.log.selectDatasetPlaceholder')"
|
||||
: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">
|
||||
<el-avatar
|
||||
v-if="!item.dataset_id && item.type === '1'"
|
||||
class="mr-12 avatar-purple"
|
||||
shape="square"
|
||||
:size="24"
|
||||
>
|
||||
<img src="@/assets/knowledge/icon_web.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<el-avatar
|
||||
v-else-if="!item.dataset_id && item.type === '2'"
|
||||
class="mr-12 avatar-purple"
|
||||
shape="square"
|
||||
:size="24"
|
||||
style="background: none"
|
||||
>
|
||||
<img src="@/assets/knowledge/logo_lark.svg" style="width: 100%" alt="" />
|
||||
</el-avatar>
|
||||
<el-avatar
|
||||
v-else-if="!item.dataset_id && item.type === '0'"
|
||||
class="mr-12 avatar-blue"
|
||||
shape="square"
|
||||
:size="24"
|
||||
>
|
||||
<img src="@/assets/knowledge/icon_document.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.log.saveToDocument')" prop="document_id">
|
||||
<el-select
|
||||
v-model="form.document_id"
|
||||
filterable
|
||||
:placeholder="$t('views.log.documentPlaceholder')"
|
||||
:loading="optionLoading"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in documentList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submitForm(formRef)" :loading="loading">
|
||||
{{ $t('views.document.setting.migration') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import paragraphApi from '@/api/shared/paragraph'
|
||||
import useStore from '@/stores'
|
||||
import { t } from '@/locales'
|
||||
const { knowledge, document } = useStore()
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id, documentId }
|
||||
} = route as any
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
const formRef = ref()
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const form = ref<any>({
|
||||
dataset_id: '',
|
||||
document_id: ''
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
dataset_id: [
|
||||
{ required: true, message: t('views.log.selectDatasetPlaceholder'), trigger: 'change' }
|
||||
],
|
||||
document_id: [{ required: true, message: t('views.log.documentPlaceholder'), trigger: 'change' }]
|
||||
})
|
||||
|
||||
const datasetList = ref<any[]>([])
|
||||
const documentList = ref<any[]>([])
|
||||
const optionLoading = ref(false)
|
||||
const paragraphList = ref<string[]>([])
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
form.value = {
|
||||
dataset_id: '',
|
||||
document_id: ''
|
||||
}
|
||||
datasetList.value = []
|
||||
documentList.value = []
|
||||
paragraphList.value = []
|
||||
formRef.value?.clearValidate()
|
||||
}
|
||||
})
|
||||
|
||||
function changeDataset(id: string) {
|
||||
form.value.document_id = ''
|
||||
getDocument(id)
|
||||
}
|
||||
|
||||
function getDocument(id: string) {
|
||||
document.asyncGetAllDocument(id, loading).then((res: any) => {
|
||||
documentList.value = res.data?.filter((v: any) => v.id !== documentId)
|
||||
})
|
||||
}
|
||||
|
||||
function getDataset() {
|
||||
knowledge.asyncGetAllKnowledge(loading).then((res: any) => {
|
||||
datasetList.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
const open = (list: any) => {
|
||||
paragraphList.value = list
|
||||
getDataset()
|
||||
formRef.value?.clearValidate()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
paragraphApi
|
||||
.putMigrateMulParagraph(
|
||||
id,
|
||||
documentId,
|
||||
form.value.dataset_id,
|
||||
form.value.document_id,
|
||||
paragraphList.value,
|
||||
loading
|
||||
)
|
||||
.then(() => {
|
||||
emit('refresh')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,371 @@
|
|||
<template>
|
||||
<div class="paragraph p-12-24">
|
||||
<div class="flex align-center" style="width: 78%">
|
||||
<back-button to="-1" style="margin-left: -4px"></back-button>
|
||||
<h3 style="display: inline-block">{{ documentDetail?.name }}</h3>
|
||||
<el-text type="info" v-if="documentDetail?.type === '1'"
|
||||
>({{ $t('views.document.form.source_url.label') }}:<el-link
|
||||
:href="documentDetail?.meta?.source_url"
|
||||
target="_blank"
|
||||
>
|
||||
<span class="break-all">{{ documentDetail?.meta?.source_url }} </span></el-link
|
||||
>)
|
||||
</el-text>
|
||||
</div>
|
||||
<div class="header-button">
|
||||
<el-button @click="batchSelectedHandle(true)" v-if="isBatch === false">
|
||||
{{ $t('views.paragraph.setting.batchSelected') }}
|
||||
</el-button>
|
||||
<el-button @click="batchSelectedHandle(false)" v-if="isBatch === true">
|
||||
{{ $t('views.paragraph.setting.cancelSelected') }}
|
||||
</el-button>
|
||||
<el-button @click="addParagraph" type="primary" :disabled="loading" v-if="isBatch === false">
|
||||
{{ $t('views.paragraph.addParagraph') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-card
|
||||
style="--el-card-padding: 0"
|
||||
class="paragraph__main mt-16"
|
||||
v-loading="(paginationConfig.current_page === 1 && loading) || changeStateloading"
|
||||
>
|
||||
<div class="flex-between p-12-16 border-b">
|
||||
<span>{{ paginationConfig.total }} {{ $t('views.paragraph.paragraph_count') }}</span>
|
||||
<el-input
|
||||
v-model="search"
|
||||
:placeholder="$t('common.search')"
|
||||
class="input-with-select"
|
||||
style="width: 260px"
|
||||
@change="searchHandle"
|
||||
clearable
|
||||
>
|
||||
<template #prepend>
|
||||
<el-select v-model="searchType" placeholder="Select" style="width: 80px">
|
||||
<el-option :label="$t('common.title')" value="title" />
|
||||
<el-option :label="$t('common.content')" value="content" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="paragraph-sidebar p-16 border-r">
|
||||
<el-anchor
|
||||
direction="vertical"
|
||||
type="default"
|
||||
:offset="130"
|
||||
container=".paragraph-scollbar"
|
||||
@click="handleClick"
|
||||
>
|
||||
<template v-for="(item, index) in paragraphDetail" :key="item.id">
|
||||
<el-anchor-link :href="`#${item.id}`" :title="item.title" v-if="item.title" />
|
||||
</template>
|
||||
</el-anchor>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<el-empty v-if="paragraphDetail.length == 0" :description="$t('common.noData')" />
|
||||
<div v-else>
|
||||
<el-scrollbar class="paragraph-scollbar">
|
||||
<div class="paragraph-detail">
|
||||
<InfiniteScroll
|
||||
:size="paragraphDetail.length"
|
||||
:total="paginationConfig.total"
|
||||
:page_size="paginationConfig.page_size"
|
||||
v-model:current_page="paginationConfig.current_page"
|
||||
@load="getParagraphList"
|
||||
:loading="loading"
|
||||
>
|
||||
<VueDraggable
|
||||
ref="el"
|
||||
v-bind:modelValue="paragraphDetail"
|
||||
:disabled="isBatch === true"
|
||||
handle=".handle"
|
||||
:animation="150"
|
||||
ghostClass="ghost"
|
||||
@end="onEnd"
|
||||
>
|
||||
<el-checkbox-group v-model="multipleSelection">
|
||||
<template v-for="(item, index) in paragraphDetail" :key="item.id">
|
||||
<!-- 批量操作 -->
|
||||
<div class="paragraph-card flex" :id="item.id" v-if="isBatch === true">
|
||||
<el-checkbox :value="item.id" />
|
||||
<ParagraphCard :data="item" class="mb-8 w-full" />
|
||||
</div>
|
||||
<!-- 非批量操作 -->
|
||||
<div class="handle paragraph-card flex" :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>
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
</VueDraggable>
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mul-operation border-t w-full" v-if="isBatch === true">
|
||||
<el-button :disabled="multipleSelection.length === 0" @click="openGenerateDialog()">
|
||||
{{ $t('views.document.generateQuestion.title') }}
|
||||
</el-button>
|
||||
<el-button :disabled="multipleSelection.length === 0" @click="openSelectDocumentDialog()">
|
||||
{{ $t('views.document.setting.migration') }}
|
||||
</el-button>
|
||||
|
||||
<el-button :disabled="multipleSelection.length === 0" @click="deleteMulParagraph">
|
||||
{{ $t('common.delete') }}
|
||||
</el-button>
|
||||
<span class="ml-8">
|
||||
{{ $t('views.document.selected') }} {{ multipleSelection.length }}
|
||||
{{ $t('views.document.items') }}
|
||||
</span>
|
||||
</div>
|
||||
</el-card>
|
||||
<ParagraphDialog ref="ParagraphDialogRef" :title="title" @refresh="refresh" />
|
||||
<SelectDocumentDialog ref="SelectDocumentDialogRef" @refresh="refreshMigrateParagraph" />
|
||||
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="refresh" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import documentApi from '@/api/shared/document'
|
||||
import paragraphApi from '@/api/shared/paragraph'
|
||||
import ParagraphDialog from './component/ParagraphDialog.vue'
|
||||
import ParagraphCard from './component/ParagraphCard.vue'
|
||||
import SelectDocumentDialog from './component/SelectDocumentDialog.vue'
|
||||
import GenerateRelatedDialog from '@/components/generate-related-dialog/index.vue'
|
||||
import { VueDraggable } from 'vue-draggable-plus'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import useStore from '@/stores'
|
||||
import { t } from '@/locales'
|
||||
const { paragraph } = useStore()
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id, documentId },
|
||||
} = route as any
|
||||
|
||||
const SelectDocumentDialogRef = ref()
|
||||
const ParagraphDialogRef = ref()
|
||||
const loading = ref(false)
|
||||
const changeStateloading = ref(false)
|
||||
const documentDetail = ref<any>({})
|
||||
const paragraphDetail = ref<any[]>([])
|
||||
const title = ref('')
|
||||
const search = ref('')
|
||||
const searchType = ref('title')
|
||||
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
// 批量操作
|
||||
const isBatch = ref(false)
|
||||
const multipleSelection = ref<any[]>([])
|
||||
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 30,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
function deleteParagraph(id: string) {
|
||||
const index = paragraphDetail.value.findIndex((v) => v.id === id)
|
||||
paragraphDetail.value.splice(index, 1)
|
||||
}
|
||||
|
||||
function changeState(id: string) {
|
||||
const index = paragraphDetail.value.findIndex((v) => v.id === id)
|
||||
paragraphDetail.value[index].is_active = !paragraphDetail.value[index].is_active
|
||||
}
|
||||
|
||||
function refreshMigrateParagraph() {
|
||||
paragraphDetail.value = paragraphDetail.value.filter(
|
||||
(v) => !multipleSelection.value.includes(v.id),
|
||||
)
|
||||
multipleSelection.value = []
|
||||
MsgSuccess(t('views.document.tip.migrationSuccess'))
|
||||
}
|
||||
|
||||
function openSelectDocumentDialog(row?: any) {
|
||||
if (row) {
|
||||
multipleSelection.value = [row.id]
|
||||
}
|
||||
SelectDocumentDialogRef.value.open(multipleSelection.value)
|
||||
}
|
||||
function deleteMulParagraph() {
|
||||
MsgConfirm(
|
||||
`${t('views.document.delete.confirmTitle1')} ${multipleSelection.value.length} ${t('views.document.delete.confirmTitle2')}`,
|
||||
t('views.paragraph.delete.confirmMessage'),
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
paragraphApi
|
||||
.putMulParagraph(id, documentId, multipleSelection.value, changeStateloading)
|
||||
.then(() => {
|
||||
paragraphDetail.value = paragraphDetail.value.filter(
|
||||
(v) => !multipleSelection.value.includes(v.id),
|
||||
)
|
||||
multipleSelection.value = []
|
||||
MsgSuccess(t('views.document.delete.successMessage'))
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function batchSelectedHandle(bool: boolean) {
|
||||
isBatch.value = bool
|
||||
multipleSelection.value = []
|
||||
}
|
||||
|
||||
function selectHandle(id: string) {
|
||||
if (multipleSelection.value.includes(id)) {
|
||||
multipleSelection.value.splice(multipleSelection.value.indexOf(id), 1)
|
||||
} else {
|
||||
multipleSelection.value.push(id)
|
||||
}
|
||||
}
|
||||
|
||||
function searchHandle() {
|
||||
paginationConfig.current_page = 1
|
||||
paragraphDetail.value = []
|
||||
getParagraphList()
|
||||
}
|
||||
|
||||
function addParagraph() {
|
||||
title.value = t('views.paragraph.addParagraph')
|
||||
ParagraphDialogRef.value.open()
|
||||
}
|
||||
|
||||
function getDetail() {
|
||||
loading.value = true
|
||||
documentApi
|
||||
.getDocumentDetail(id, documentId)
|
||||
.then((res) => {
|
||||
documentDetail.value = res.data
|
||||
loading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function getParagraphList() {
|
||||
paragraphApi
|
||||
.getParagraph(
|
||||
id,
|
||||
documentId,
|
||||
paginationConfig,
|
||||
search.value && { [searchType.value]: search.value },
|
||||
loading,
|
||||
)
|
||||
.then((res) => {
|
||||
paragraphDetail.value = [...paragraphDetail.value, ...res.data.records]
|
||||
paginationConfig.total = res.data.total
|
||||
})
|
||||
}
|
||||
|
||||
function refresh(data: any) {
|
||||
if (data) {
|
||||
const index = paragraphDetail.value.findIndex((v) => v.id === data.id)
|
||||
paragraphDetail.value.splice(index, 1, data)
|
||||
} else {
|
||||
paginationConfig.current_page = 1
|
||||
paragraphDetail.value = []
|
||||
getParagraphList()
|
||||
}
|
||||
}
|
||||
|
||||
const GenerateRelatedDialogRef = ref()
|
||||
function openGenerateDialog(row?: any) {
|
||||
const arr: string[] = []
|
||||
if (row) {
|
||||
arr.push(row.id)
|
||||
} else {
|
||||
multipleSelection.value.map((v) => {
|
||||
if (v) {
|
||||
arr.push(v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
GenerateRelatedDialogRef.value.open(arr, 'paragraph')
|
||||
}
|
||||
|
||||
function onEnd(event?: any) {
|
||||
const { oldIndex, newIndex } = event
|
||||
if (oldIndex === undefined || newIndex === undefined) return
|
||||
const list = cloneDeep(paragraphDetail.value)
|
||||
if (oldIndex === list.length - 1 || newIndex === list.length - 1) {
|
||||
return
|
||||
}
|
||||
const newInstance = { ...list[oldIndex], type: list[newIndex].type, id: list[newIndex].id }
|
||||
const oldInstance = { ...list[newIndex], type: list[oldIndex].type, id: list[oldIndex].id }
|
||||
list[newIndex] = newInstance
|
||||
list[oldIndex] = oldInstance
|
||||
paragraphDetail.value = list
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDetail()
|
||||
getParagraphList()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.paragraph {
|
||||
position: relative;
|
||||
.header-button {
|
||||
position: absolute;
|
||||
right: calc(var(--app-base-px) * 3);
|
||||
top: calc(var(--app-base-px) + 4px);
|
||||
}
|
||||
.paragraph-sidebar {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.paragraph-detail {
|
||||
height: calc(100vh - 215px);
|
||||
max-width: 1000px;
|
||||
margin: 16px auto;
|
||||
}
|
||||
|
||||
&__main {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
.mul-operation {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 16px 24px;
|
||||
box-sizing: border-box;
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
.paragraph-card {
|
||||
&.handle {
|
||||
.handle-img {
|
||||
visibility: hidden;
|
||||
}
|
||||
&:hover {
|
||||
.handle-img {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.problem.createProblem')"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<el-form
|
||||
label-position="top"
|
||||
ref="problemFormRef"
|
||||
:rules="rules"
|
||||
:model="form"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
<el-form-item :label="$t('views.problem.title')" prop="data">
|
||||
<el-input
|
||||
v-model="form.data"
|
||||
:placeholder="$t('views.problem.tip.placeholder')"
|
||||
:rows="10"
|
||||
type="textarea"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit(problemFormRef)" :loading="loading">
|
||||
{{ $t('common.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import { t } from '@/locales'
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }
|
||||
} = route as any
|
||||
const { problem } = useStore()
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
const problemFormRef = ref()
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
const form = ref<any>({
|
||||
data: ''
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
data: [{ required: true, message: t('views.problem.tip.requiredMessage'), trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
form.value = {
|
||||
data: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const open = () => {
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
const arr = form.value.data.split('\n').filter(function (item: string) {
|
||||
return item !== ''
|
||||
})
|
||||
problem.asyncPostProblem(id, arr, loading).then((res: any) => {
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
emit('refresh')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
<template>
|
||||
<el-drawer v-model="visible" size="60%" @close="closeHandle">
|
||||
<template #header>
|
||||
<h4>{{ $t('views.problem.detailProblem') }}</h4>
|
||||
</template>
|
||||
<div>
|
||||
<el-scrollbar>
|
||||
<div class="p-8">
|
||||
<el-form label-position="top" v-loading="loading" @submit.prevent>
|
||||
<el-form-item :label="$t('views.problem.title')">
|
||||
<ReadWrite
|
||||
@change="editName"
|
||||
:data="currentContent"
|
||||
:showEditIcon="true"
|
||||
:maxlength="256"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.problem.relateParagraph.title')">
|
||||
<template v-for="(item, index) in paragraphList" :key="index">
|
||||
<CardBox
|
||||
:title="item.title || '-'"
|
||||
class="paragraph-source-card cursor mb-8"
|
||||
:showIcon="false"
|
||||
@click.stop="editParagraph(item)"
|
||||
>
|
||||
<div class="active-button">
|
||||
<span class="mr-4">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('views.problem.setting.cancelRelated')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button type="primary" text @click.stop="disassociation(item)">
|
||||
<AppIcon iconName="app-quxiaoguanlian"></AppIcon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</div>
|
||||
<template #description>
|
||||
<el-scrollbar height="80">
|
||||
{{ item.content }}
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<el-text>
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
{{ item?.document_name }}
|
||||
</el-text>
|
||||
</div>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<ParagraphDialog
|
||||
ref="ParagraphDialogRef"
|
||||
:title="$t('views.paragraph.editParagraph')"
|
||||
@refresh="refresh"
|
||||
/>
|
||||
<RelateProblemDialog ref="RelateProblemDialogRef" @refresh="refresh" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="relateProblem">{{
|
||||
$t('views.problem.relateParagraph.title')
|
||||
}}</el-button>
|
||||
<el-button @click="pre" :disabled="pre_disable || loading">{{
|
||||
$t('views.log.buttons.prev')
|
||||
}}</el-button>
|
||||
<el-button @click="next" :disabled="next_disable || loading">{{
|
||||
$t('views.log.buttons.next')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import problemApi from '@/api/shared/problem'
|
||||
import ParagraphDialog from '@/views/paragraph/component/ParagraphDialog.vue'
|
||||
import RelateProblemDialog from './RelateProblemDialog.vue'
|
||||
import { MsgSuccess, MsgConfirm, MsgError } from '@/utils/message'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import { t } from '@/locales'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/**
|
||||
* 当前的id
|
||||
*/
|
||||
currentId: string
|
||||
currentContent: string
|
||||
/**
|
||||
* 下一条
|
||||
*/
|
||||
next: () => void
|
||||
/**
|
||||
* 上一条
|
||||
*/
|
||||
pre: () => void
|
||||
|
||||
pre_disable: boolean
|
||||
|
||||
next_disable: boolean
|
||||
}>(),
|
||||
{}
|
||||
)
|
||||
|
||||
const emit = defineEmits(['update:currentId', 'update:currentContent', 'refresh'])
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }
|
||||
} = route
|
||||
|
||||
const { problem } = useStore()
|
||||
const RelateProblemDialogRef = ref()
|
||||
const ParagraphDialogRef = ref()
|
||||
const loading = ref(false)
|
||||
const visible = ref(false)
|
||||
const paragraphList = ref<any[]>([])
|
||||
|
||||
function disassociation(item: any) {
|
||||
problem
|
||||
.asyncDisassociationProblem(
|
||||
item.dataset_id,
|
||||
item.document_id,
|
||||
item.id,
|
||||
props.currentId,
|
||||
loading
|
||||
)
|
||||
.then(() => {
|
||||
getRecord()
|
||||
})
|
||||
}
|
||||
|
||||
function relateProblem() {
|
||||
RelateProblemDialogRef.value.open([props.currentId])
|
||||
}
|
||||
|
||||
function editParagraph(row: any) {
|
||||
ParagraphDialogRef.value.open(row)
|
||||
}
|
||||
|
||||
function editName(val: string) {
|
||||
if (val) {
|
||||
const obj = {
|
||||
content: val
|
||||
}
|
||||
problemApi.putProblems(id as string, props.currentId, obj, loading).then(() => {
|
||||
emit('update:currentContent', val)
|
||||
MsgSuccess(t('common.modifySuccess'))
|
||||
})
|
||||
} else {
|
||||
MsgError(t('views.problem.tip.errorMessage'))
|
||||
}
|
||||
}
|
||||
|
||||
function closeHandle() {
|
||||
paragraphList.value = []
|
||||
}
|
||||
|
||||
function getRecord() {
|
||||
if (props.currentId && visible.value) {
|
||||
problemApi.getDetailProblems(id as string, props.currentId, loading).then((res) => {
|
||||
paragraphList.value = res.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
getRecord()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.currentId,
|
||||
() => {
|
||||
paragraphList.value = []
|
||||
getRecord()
|
||||
}
|
||||
)
|
||||
|
||||
watch(visible, (bool) => {
|
||||
if (!bool) {
|
||||
emit('update:currentId', '')
|
||||
emit('update:currentContent', '')
|
||||
emit('refresh')
|
||||
}
|
||||
})
|
||||
|
||||
const open = () => {
|
||||
getRecord()
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -0,0 +1,330 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.problem.relateParagraph.title')"
|
||||
v-model="dialogVisible"
|
||||
width="80%"
|
||||
class="paragraph-dialog"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<el-row v-loading="loading">
|
||||
<el-col :span="6">
|
||||
<el-scrollbar height="500" wrap-class="paragraph-scrollbar">
|
||||
<div class="bold title align-center p-24 pb-0">
|
||||
{{ $t('views.problem.relateParagraph.selectDocument') }}
|
||||
</div>
|
||||
<div class="p-8" style="padding-bottom: 8px">
|
||||
<el-input
|
||||
v-model="filterDoc"
|
||||
:placeholder="$t('views.problem.relateParagraph.placeholder')"
|
||||
prefix-icon="Search"
|
||||
clearable
|
||||
/>
|
||||
<common-list
|
||||
:data="documentList"
|
||||
class="mt-8"
|
||||
@click="clickDocumentHandle"
|
||||
:default-active="currentDocument"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span class="flex lighter align-center">
|
||||
<auto-tooltip :content="row.name">
|
||||
{{ row.name }}
|
||||
</auto-tooltip>
|
||||
<el-badge
|
||||
:value="associationCount(row.id)"
|
||||
type="primary"
|
||||
v-if="associationCount(row.id)"
|
||||
class="paragraph-badge ml-4"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</common-list>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-col>
|
||||
<el-col :span="18" class="border-l">
|
||||
<el-scrollbar height="500" wrap-class="paragraph-scrollbar">
|
||||
<div class="p-24" style="padding-bottom: 8px; padding-top: 16px">
|
||||
<div class="flex-between mb-16">
|
||||
<div class="bold title align-center">
|
||||
{{ $t('components.selectParagraph.title') }}
|
||||
<el-text>
|
||||
({{ $t('views.problem.relateParagraph.selectedParagraph') }}:{{
|
||||
associationCount(currentDocument)
|
||||
}}
|
||||
{{ $t('views.problem.relateParagraph.count') }})
|
||||
</el-text>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="search"
|
||||
:placeholder="$t('common.search')"
|
||||
class="input-with-select"
|
||||
style="width: 260px"
|
||||
@change="searchHandle"
|
||||
>
|
||||
<template #prepend>
|
||||
<el-select v-model="searchType" placeholder="Select" style="width: 80px">
|
||||
<el-option :label="$t('common.title')" value="title" />
|
||||
<el-option :label="$t('common.content')" value="content" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-empty v-if="paragraphList.length == 0" :description="$t('common.noData')" />
|
||||
|
||||
<InfiniteScroll
|
||||
v-else
|
||||
:size="paragraphList.length"
|
||||
:total="paginationConfig.total"
|
||||
:page_size="paginationConfig.page_size"
|
||||
v-model:current_page="paginationConfig.current_page"
|
||||
@load="getParagraphList"
|
||||
:loading="loading"
|
||||
>
|
||||
<template v-for="(item, index) in paragraphList" :key="index">
|
||||
<CardBox
|
||||
shadow="hover"
|
||||
:title="item.title || '-'"
|
||||
:description="item.content"
|
||||
class="paragraph-card cursor mb-16"
|
||||
:class="isAssociation(item.id) ? 'selected' : ''"
|
||||
:showIcon="false"
|
||||
@click="associationClick(item)"
|
||||
>
|
||||
</CardBox>
|
||||
</template>
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer v-if="isMul">
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false"> {{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="mulAssociation"> {{ $t('common.confirm') }} </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, reactive } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import problemApi from '@/api/shared/problem'
|
||||
import paragraphApi from '@/api/shared/paragraph'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
const { problem, document } = useStore()
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }, // knowledgeId
|
||||
} = route as any
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
const loading = ref(false)
|
||||
const documentList = ref<any[]>([])
|
||||
const cloneDocumentList = ref<any[]>([])
|
||||
const paragraphList = ref<any[]>([])
|
||||
const currentProblemId = ref<string>('')
|
||||
const currentMulProblemId = ref<string[]>([])
|
||||
|
||||
// 回显
|
||||
const associationParagraph = ref<any[]>([])
|
||||
|
||||
const currentDocument = ref<string>('')
|
||||
const search = ref('')
|
||||
const searchType = ref('title')
|
||||
const filterDoc = ref('')
|
||||
// 批量
|
||||
const isMul = ref(false)
|
||||
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 50,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
function mulAssociation() {
|
||||
const data = {
|
||||
problem_id_list: currentMulProblemId.value,
|
||||
paragraph_list: associationParagraph.value.map((item) => ({
|
||||
paragraph_id: item.id,
|
||||
document_id: item.document_id,
|
||||
})),
|
||||
}
|
||||
problemApi.putMulAssociationProblem(id, data, loading).then(() => {
|
||||
MsgSuccess(t('views.problem.tip.relatedSuccess'))
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function associationClick(item: any) {
|
||||
if (isMul.value) {
|
||||
if (isAssociation(item.id)) {
|
||||
associationParagraph.value.splice(associationParagraph.value.indexOf(item.id), 1)
|
||||
} else {
|
||||
associationParagraph.value.push(item)
|
||||
}
|
||||
} else {
|
||||
if (isAssociation(item.id)) {
|
||||
problem
|
||||
.asyncDisassociationProblem(
|
||||
id,
|
||||
item.document_id,
|
||||
item.id,
|
||||
currentProblemId.value as string,
|
||||
loading,
|
||||
)
|
||||
.then(() => {
|
||||
getRecord(currentProblemId.value)
|
||||
})
|
||||
} else {
|
||||
problem
|
||||
.asyncAssociationProblem(
|
||||
id,
|
||||
item.document_id,
|
||||
item.id,
|
||||
currentProblemId.value as string,
|
||||
loading,
|
||||
)
|
||||
.then(() => {
|
||||
getRecord(currentProblemId.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function searchHandle() {
|
||||
paginationConfig.current_page = 1
|
||||
paragraphList.value = []
|
||||
currentDocument.value && getParagraphList(currentDocument.value)
|
||||
}
|
||||
|
||||
function clickDocumentHandle(item: any) {
|
||||
paginationConfig.current_page = 1
|
||||
paragraphList.value = []
|
||||
currentDocument.value = item.id
|
||||
getParagraphList(item.id)
|
||||
}
|
||||
|
||||
function getDocument() {
|
||||
document.asyncGetAllDocument(id, loading).then((res: any) => {
|
||||
cloneDocumentList.value = res.data
|
||||
documentList.value = res.data
|
||||
currentDocument.value = cloneDocumentList.value?.length > 0 ? cloneDocumentList.value[0].id : ''
|
||||
currentDocument.value && getParagraphList(currentDocument.value)
|
||||
})
|
||||
}
|
||||
|
||||
function getParagraphList(documentId: string) {
|
||||
paragraphApi
|
||||
.getParagraph(
|
||||
id,
|
||||
(documentId || currentDocument.value) as string,
|
||||
paginationConfig,
|
||||
search.value && { [searchType.value]: search.value },
|
||||
loading,
|
||||
)
|
||||
.then((res) => {
|
||||
paragraphList.value = [...paragraphList.value, ...res.data.records]
|
||||
paginationConfig.total = res.data.total
|
||||
})
|
||||
}
|
||||
|
||||
// 已关联分段
|
||||
function getRecord(problemId: string) {
|
||||
problemApi.getDetailProblems(id as string, problemId as string, loading).then((res) => {
|
||||
associationParagraph.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
function associationCount(documentId: string) {
|
||||
return associationParagraph.value.filter((item) => item.document_id === documentId).length
|
||||
}
|
||||
function isAssociation(paragraphId: string) {
|
||||
return associationParagraph.value.some((option) => option.id === paragraphId)
|
||||
}
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
documentList.value = []
|
||||
cloneDocumentList.value = []
|
||||
paragraphList.value = []
|
||||
associationParagraph.value = []
|
||||
isMul.value = false
|
||||
|
||||
currentDocument.value = ''
|
||||
search.value = ''
|
||||
searchType.value = 'title'
|
||||
emit('refresh')
|
||||
}
|
||||
})
|
||||
|
||||
watch(filterDoc, (val) => {
|
||||
paragraphList.value = []
|
||||
documentList.value = val
|
||||
? cloneDocumentList.value.filter((item) => item.name.includes(val))
|
||||
: cloneDocumentList.value
|
||||
currentDocument.value = documentList.value?.length > 0 ? documentList.value[0].id : ''
|
||||
})
|
||||
|
||||
const open = (problemId: any) => {
|
||||
getDocument()
|
||||
if (problemId.length == 1) {
|
||||
currentProblemId.value = problemId[0]
|
||||
getRecord(problemId)
|
||||
} else if (problemId.length > 1) {
|
||||
currentMulProblemId.value = problemId
|
||||
isMul.value = true
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.paragraph-card {
|
||||
position: relative;
|
||||
// card 选中样式
|
||||
&.selected {
|
||||
border: 1px solid var(--el-color-primary) !important;
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border: 14px solid var(--el-color-primary);
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: transparent;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
width: 3px;
|
||||
height: 6px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 2px;
|
||||
border: 2px solid #fff;
|
||||
border-top-color: transparent;
|
||||
border-left-color: transparent;
|
||||
transform: rotate(35deg);
|
||||
}
|
||||
&:hover {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.paragraph-badge {
|
||||
.el-badge__content {
|
||||
height: auto;
|
||||
display: table;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,388 @@
|
|||
<template>
|
||||
<div class="document p-16-24">
|
||||
<h2 class="mb-16">{{ $t('views.problem.title') }}</h2>
|
||||
<el-card style="--el-card-padding: 0">
|
||||
<div class="main-calc-height">
|
||||
<div class="p-24">
|
||||
<div class="flex-between">
|
||||
<div>
|
||||
<el-button type="primary" @click="createProblem"
|
||||
>{{ $t('views.problem.createProblem') }}
|
||||
</el-button>
|
||||
<el-button @click="relateProblem()" :disabled="multipleSelection.length === 0"
|
||||
>{{ $t('views.problem.relateParagraph.title') }}
|
||||
</el-button>
|
||||
<el-button @click="deleteMulDocument" :disabled="multipleSelection.length === 0"
|
||||
>{{ $t('views.problem.setting.batchDelete') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-input
|
||||
v-model="filterText"
|
||||
:placeholder="$t('views.problem.searchBar.placeholder')"
|
||||
prefix-icon="Search"
|
||||
class="w-240"
|
||||
@change="getList"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<app-table
|
||||
ref="multipleTableRef"
|
||||
class="mt-16"
|
||||
:data="problemData"
|
||||
:pagination-config="paginationConfig"
|
||||
quick-create
|
||||
:quickCreateName="$t('views.problem.quickCreateName')"
|
||||
:quickCreatePlaceholder="$t('views.problem.quickCreateProblem')"
|
||||
:quickCreateMaxlength="256"
|
||||
@sizeChange="handleSizeChange"
|
||||
@changePage="getList"
|
||||
@cell-mouse-enter="cellMouseEnter"
|
||||
@cell-mouse-leave="cellMouseLeave"
|
||||
@creatQuick="creatQuickHandle"
|
||||
@row-click="rowClickHandle"
|
||||
@selection-change="handleSelectionChange"
|
||||
:row-class-name="setRowClass"
|
||||
v-loading="loading"
|
||||
:row-key="(row: any) => row.id"
|
||||
>
|
||||
<el-table-column type="selection" width="55" :reserve-selection="true" />
|
||||
<el-table-column prop="content" :label="$t('views.problem.title')" min-width="280">
|
||||
<template #default="{ row }">
|
||||
<ReadWrite
|
||||
@change="editName($event, row.id)"
|
||||
:data="row.content"
|
||||
:showEditIcon="row.id === currentMouseId"
|
||||
:maxlength="256"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="paragraph_count"
|
||||
:label="$t('views.problem.table.paragraph_count')"
|
||||
align="right"
|
||||
min-width="100"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-link
|
||||
type="primary"
|
||||
@click.stop="rowClickHandle(row)"
|
||||
v-if="row.paragraph_count"
|
||||
>
|
||||
{{ row.paragraph_count }}
|
||||
</el-link>
|
||||
<span v-else>
|
||||
{{ row.paragraph_count }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" :label="$t('common.createTime')" width="170">
|
||||
<template #default="{ row }">
|
||||
{{ datetimeFormat(row.create_time) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="update_time"
|
||||
:label="$t('views.problem.table.updateTime')"
|
||||
width="170"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ datetimeFormat(row.update_time) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.operation')" align="left" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<span class="mr-4">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('views.problem.relateParagraph.title')"
|
||||
placement="top"
|
||||
>
|
||||
<el-button type="primary" text @click.stop="relateProblem(row)">
|
||||
<el-icon><Connection /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span>
|
||||
<el-tooltip effect="dark" :content="$t('common.delete')" placement="top">
|
||||
<el-button type="primary" text @click.stop="deleteProblem(row)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</app-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<CreateProblemDialog ref="CreateProblemDialogRef" @refresh="refresh" />
|
||||
<DetailProblemDrawer
|
||||
:next="nextChatRecord"
|
||||
:pre="preChatRecord"
|
||||
ref="DetailProblemRef"
|
||||
v-model:currentId="currentClickId"
|
||||
v-model:currentContent="currentContent"
|
||||
:pre_disable="pre_disable"
|
||||
:next_disable="next_disable"
|
||||
@refresh="refreshRelate"
|
||||
/>
|
||||
<RelateProblemDialog ref="RelateProblemDialogRef" @refresh="refreshRelate" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive, onBeforeUnmount, computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ElTable } from 'element-plus'
|
||||
import problemApi from '@/api/shared/problem'
|
||||
import CreateProblemDialog from './component/CreateProblemDialog.vue'
|
||||
import DetailProblemDrawer from './component/DetailProblemDrawer.vue'
|
||||
import RelateProblemDialog from './component/RelateProblemDialog.vue'
|
||||
import { datetimeFormat } from '@/utils/time'
|
||||
import { MsgSuccess, MsgConfirm, MsgError } from '@/utils/message'
|
||||
import type { Dict } from '@/api/type/common'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }, // 知识库id
|
||||
} = route as any
|
||||
|
||||
const { problem } = useStore()
|
||||
|
||||
const RelateProblemDialogRef = ref()
|
||||
const DetailProblemRef = ref()
|
||||
const CreateProblemDialogRef = ref()
|
||||
const loading = ref(false)
|
||||
|
||||
// 当前需要修改问题的id
|
||||
const currentMouseId = ref('')
|
||||
// 当前点击打开drawer的id
|
||||
const currentClickId = ref('')
|
||||
const currentContent = ref('')
|
||||
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 10,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const filterText = ref('')
|
||||
const problemData = ref<any[]>([])
|
||||
const problemIndexMap = computed<Dict<number>>(() => {
|
||||
return problemData.value
|
||||
.map((row, index) => ({
|
||||
[row.id]: index,
|
||||
}))
|
||||
.reduce((pre, next) => ({ ...pre, ...next }), {})
|
||||
})
|
||||
|
||||
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
||||
const multipleSelection = ref<any[]>([])
|
||||
|
||||
function relateProblem(row?: any) {
|
||||
const arr: string[] = []
|
||||
if (row) {
|
||||
arr.push(row.id)
|
||||
} else {
|
||||
multipleSelection.value.map((v) => {
|
||||
if (v) {
|
||||
arr.push(v.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
RelateProblemDialogRef.value.open(arr)
|
||||
}
|
||||
|
||||
function createProblem() {
|
||||
CreateProblemDialogRef.value.open()
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: any[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
/*
|
||||
快速创建空白文档
|
||||
*/
|
||||
function creatQuickHandle(val: string) {
|
||||
loading.value = true
|
||||
const obj = [val]
|
||||
problem
|
||||
.asyncPostProblem(id, obj)
|
||||
.then((res) => {
|
||||
getList()
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function deleteMulDocument() {
|
||||
const arr: string[] = []
|
||||
multipleSelection.value.map((v) => {
|
||||
if (v) {
|
||||
arr.push(v.id)
|
||||
}
|
||||
})
|
||||
problemApi.putMulProblem(id, arr, loading).then(() => {
|
||||
MsgSuccess(t('views.document.delete.successMessage'))
|
||||
multipleTableRef.value?.clearSelection()
|
||||
getList()
|
||||
})
|
||||
}
|
||||
|
||||
function deleteProblem(row: any) {
|
||||
MsgConfirm(
|
||||
`${t('views.problem.delete.confirmTitle')} ${row.content} ?`,
|
||||
`${t('views.problem.delete.confirmMessage1')} ${row.paragraph_count} ${t('views.problem.delete.confirmMessage2')}`,
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
problemApi.delProblems(id, row.id, loading).then(() => {
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
getList()
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function editName(val: string, problemId: string) {
|
||||
if (val) {
|
||||
const obj = {
|
||||
content: val,
|
||||
}
|
||||
problemApi.putProblems(id, problemId, obj, loading).then(() => {
|
||||
getList()
|
||||
MsgSuccess(t('common.modifySuccess'))
|
||||
})
|
||||
} else {
|
||||
MsgError(t('views.problem.tip.errorMessage'))
|
||||
}
|
||||
}
|
||||
|
||||
function cellMouseEnter(row: any) {
|
||||
currentMouseId.value = row.id
|
||||
}
|
||||
|
||||
function cellMouseLeave() {
|
||||
currentMouseId.value = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一页
|
||||
*/
|
||||
const nextChatRecord = () => {
|
||||
let index = problemIndexMap.value[currentClickId.value] + 1
|
||||
if (index >= problemData.value.length) {
|
||||
if (
|
||||
index + (paginationConfig.current_page - 1) * paginationConfig.page_size >=
|
||||
paginationConfig.total - 1
|
||||
) {
|
||||
return
|
||||
}
|
||||
paginationConfig.current_page = paginationConfig.current_page + 1
|
||||
getList().then(() => {
|
||||
index = 0
|
||||
currentClickId.value = problemData.value[index].id
|
||||
currentContent.value = problemData.value[index].content
|
||||
})
|
||||
} else {
|
||||
currentClickId.value = problemData.value[index].id
|
||||
currentContent.value = problemData.value[index].content
|
||||
}
|
||||
}
|
||||
const pre_disable = computed(() => {
|
||||
const index = problemIndexMap.value[currentClickId.value] - 1
|
||||
return index < 0 && paginationConfig.current_page <= 1
|
||||
})
|
||||
|
||||
const next_disable = computed(() => {
|
||||
const index = problemIndexMap.value[currentClickId.value] + 1
|
||||
return (
|
||||
index >= problemData.value.length &&
|
||||
index + (paginationConfig.current_page - 1) * paginationConfig.page_size >=
|
||||
paginationConfig.total - 1
|
||||
)
|
||||
})
|
||||
/**
|
||||
* 上一页
|
||||
*/
|
||||
const preChatRecord = () => {
|
||||
let index = problemIndexMap.value[currentClickId.value] - 1
|
||||
|
||||
if (index < 0) {
|
||||
if (paginationConfig.current_page <= 1) {
|
||||
return
|
||||
}
|
||||
paginationConfig.current_page = paginationConfig.current_page - 1
|
||||
getList().then((ok) => {
|
||||
index = paginationConfig.page_size - 1
|
||||
currentClickId.value = problemData.value[index].id
|
||||
currentContent.value = problemData.value[index].content
|
||||
})
|
||||
} else {
|
||||
currentClickId.value = problemData.value[index].id
|
||||
currentContent.value = problemData.value[index].content
|
||||
}
|
||||
}
|
||||
|
||||
function rowClickHandle(row: any, column?: any) {
|
||||
if (column && column.type === 'selection') {
|
||||
return
|
||||
}
|
||||
if (row.paragraph_count) {
|
||||
currentClickId.value = row.id
|
||||
currentContent.value = row.content
|
||||
DetailProblemRef.value.open()
|
||||
}
|
||||
}
|
||||
|
||||
const setRowClass = ({ row }: any) => {
|
||||
return currentClickId.value === row?.id ? 'highlight' : ''
|
||||
}
|
||||
|
||||
function handleSizeChange() {
|
||||
paginationConfig.current_page = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
return problem
|
||||
.asyncGetProblem(
|
||||
id as string,
|
||||
paginationConfig,
|
||||
filterText.value && { content: filterText.value },
|
||||
loading,
|
||||
)
|
||||
.then((res: any) => {
|
||||
problemData.value = res.data.records
|
||||
paginationConfig.total = res.data.total
|
||||
})
|
||||
}
|
||||
|
||||
function refreshRelate() {
|
||||
getList()
|
||||
multipleTableRef.value?.clearSelection()
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
paginationConfig.current_page = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
<template>
|
||||
<el-drawer v-model="debugVisible" size="60%" :append-to-body="true">
|
||||
<template #header>
|
||||
<div class="flex align-center" style="margin-left: -8px">
|
||||
<el-button class="cursor mr-4" link @click.prevent="debugVisible = false">
|
||||
<el-icon :size="20">
|
||||
<Back />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<h4>{{ $t('common.debug') }}</h4>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div v-if="form.init_field_list.length > 0" class="mb-16">
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('common.param.initParam') }}
|
||||
</h4>
|
||||
<el-card shadow="never" class="card-never" style="--el-card-padding: 12px">
|
||||
<DynamicsForm
|
||||
v-model="form.init_params"
|
||||
:model="form.init_params"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:render_data="form.init_field_list"
|
||||
ref="dynamicsFormRef"
|
||||
>
|
||||
</DynamicsForm>
|
||||
</el-card>
|
||||
</div>
|
||||
<div v-if="form.debug_field_list.length > 0" class="mb-16">
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('common.param.inputParam') }}
|
||||
</h4>
|
||||
<el-card shadow="never" class="card-never" style="--el-card-padding: 12px">
|
||||
<el-form
|
||||
ref="FormRef"
|
||||
:model="form"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
hide-required-asterisk
|
||||
v-loading="loading"
|
||||
@submit.prevent
|
||||
>
|
||||
<template v-for="(item, index) in form.debug_field_list" :key="index">
|
||||
<el-form-item
|
||||
:label="item.name"
|
||||
:prop="'debug_field_list.' + index + '.value'"
|
||||
:rules="{
|
||||
required: item.is_required,
|
||||
message: $t('views.tool.form.param.inputPlaceholder'),
|
||||
trigger: 'blur',
|
||||
}"
|
||||
>
|
||||
<template #label>
|
||||
<div class="flex">
|
||||
<span
|
||||
>{{ item.name }} <span class="danger" v-if="item.is_required">*</span></span
|
||||
>
|
||||
<el-tag type="info" class="info-tag ml-4">{{ item.type }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="item.value"
|
||||
:placeholder="$t('views.tool.form.param.inputPlaceholder')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-button type="primary" @click="submit(FormRef)" :loading="loading">
|
||||
{{ $t('views.tool.form.debug.run') }}
|
||||
</el-button>
|
||||
<div v-if="showResult" class="mt-8">
|
||||
<h4 class="title-decoration-1 mb-16 mt-16">
|
||||
{{ $t('views.tool.form.debug.runResult') }}
|
||||
</h4>
|
||||
<div class="mb-16">
|
||||
<el-alert
|
||||
v-if="isSuccess"
|
||||
:title="$t('views.tool.form.debug.runSuccess')"
|
||||
type="success"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
<el-alert
|
||||
v-else
|
||||
:title="$t('views.tool.form.debug.runFailed')"
|
||||
type="error"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p class="lighter mb-8">{{ $t('views.tool.form.debug.output') }}</p>
|
||||
|
||||
<el-card
|
||||
:class="isSuccess ? '' : 'danger'"
|
||||
class="pre-wrap"
|
||||
shadow="never"
|
||||
style="max-height: 350px; overflow: scroll"
|
||||
>
|
||||
{{ String(result) == '0' ? 0 : result || '-' }}
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||
|
||||
const FormRef = ref()
|
||||
const dynamicsFormRef = ref()
|
||||
const loading = ref(false)
|
||||
const debugVisible = ref(false)
|
||||
const showResult = ref(false)
|
||||
const isSuccess = ref(false)
|
||||
const result = ref('')
|
||||
|
||||
const form = ref<any>({
|
||||
debug_field_list: [],
|
||||
code: '',
|
||||
input_field_list: [],
|
||||
init_field_list: [],
|
||||
init_params: {},
|
||||
})
|
||||
|
||||
watch(debugVisible, (bool) => {
|
||||
if (!bool) {
|
||||
showResult.value = false
|
||||
isSuccess.value = false
|
||||
result.value = ''
|
||||
form.value = {
|
||||
debug_field_list: [],
|
||||
code: '',
|
||||
input_field_list: [],
|
||||
init_field_list: [],
|
||||
init_params: {},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
const validate = formEl ? formEl.validate() : Promise.resolve()
|
||||
Promise.all([dynamicsFormRef.value?.validate(), validate]).then(() => {
|
||||
ToolApi.postToolDebug(form.value, loading).then((res) => {
|
||||
if (res.code === 500) {
|
||||
showResult.value = true
|
||||
isSuccess.value = false
|
||||
result.value = res.message
|
||||
} else {
|
||||
showResult.value = true
|
||||
isSuccess.value = true
|
||||
result.value = res.data
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const open = (data: any) => {
|
||||
if (data.input_field_list.length > 0) {
|
||||
data.input_field_list.forEach((item: any) => {
|
||||
form.value.debug_field_list.push({
|
||||
value: '',
|
||||
...item,
|
||||
})
|
||||
})
|
||||
}
|
||||
form.value.code = data.code
|
||||
form.value.input_field_list = data.input_field_list
|
||||
form.value.init_field_list = data.init_field_list
|
||||
debugVisible.value = true
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
})
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -0,0 +1,449 @@
|
|||
<template>
|
||||
<el-drawer v-model="visible" size="60%" :before-close="close">
|
||||
<template #header>
|
||||
<h4>{{ title }}</h4>
|
||||
</template>
|
||||
<div>
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('views.tool.form.title.baseInfo') }}
|
||||
</h4>
|
||||
<el-form
|
||||
ref="FormRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
v-loading="loading"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item :label="$t('views.tool.form.toolName.label')" prop="name">
|
||||
<div class="flex w-full">
|
||||
<div
|
||||
v-if="form.id"
|
||||
class="edit-avatar mr-12"
|
||||
@mouseenter="showEditIcon = true"
|
||||
@mouseleave="showEditIcon = false"
|
||||
>
|
||||
|
||||
<el-Avatar
|
||||
v-if="isAppIcon(form.icon)"
|
||||
:id="form.id"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img :src="String(form.icon)" alt=""/>
|
||||
</el-Avatar>
|
||||
<el-avatar v-else class="avatar-green" shape="square" :size="32">
|
||||
<img src="@/assets/node/icon_tool.svg" style="width: 58%" alt=""/>
|
||||
</el-avatar>
|
||||
<el-Avatar
|
||||
v-if="showEditIcon"
|
||||
:id="form.id"
|
||||
shape="square"
|
||||
class="edit-mask"
|
||||
:size="32"
|
||||
@click="openEditAvatar"
|
||||
>
|
||||
<el-icon>
|
||||
<EditPen/>
|
||||
</el-icon>
|
||||
</el-Avatar>
|
||||
</div>
|
||||
<el-avatar v-else class="avatar-green" shape="square" :size="32">
|
||||
<img src="@/assets/node/icon_tool.svg" style="width: 58%" alt=""/>
|
||||
</el-avatar>
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
:placeholder="$t('views.tool.form.toolName.placeholder')"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
@blur="form.name = form.name?.trim()"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('views.tool.form.toolDescription.label')">
|
||||
<el-input
|
||||
v-model="form.desc"
|
||||
type="textarea"
|
||||
:placeholder="$t('views.tool.form.toolDescription.placeholder')"
|
||||
maxlength="128"
|
||||
show-word-limit
|
||||
:autosize="{ minRows: 3 }"
|
||||
@blur="form.desc = form.desc?.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="flex-between">
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('common.param.initParam') }}
|
||||
</h4>
|
||||
<el-button link type="primary" @click="openAddInitDialog()">
|
||||
<el-icon class="mr-4">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
{{ $t('common.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table ref="initFieldTableRef" :data="form.init_field_list" class="mb-16">
|
||||
<el-table-column prop="field" :label="$t('dynamicsForm.paramForm.field.label')">
|
||||
<template #default="{ row }">
|
||||
<span :title="row.field" class="ellipsis-1">{{ row.field }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('dynamicsForm.paramForm.input_type.label')">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'TextInput'">{{
|
||||
$t('dynamicsForm.input_type_list.TextInput')
|
||||
}}
|
||||
</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'PasswordInput'">{{
|
||||
$t('dynamicsForm.input_type_list.PasswordInput')
|
||||
}}
|
||||
</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'Slider'">{{
|
||||
$t('dynamicsForm.input_type_list.Slider')
|
||||
}}
|
||||
</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'SwitchInput'">{{
|
||||
$t('dynamicsForm.input_type_list.SwitchInput')
|
||||
}}
|
||||
</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'SingleSelect'">{{
|
||||
$t('dynamicsForm.input_type_list.SingleSelect')
|
||||
}}
|
||||
</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'MultiSelect'">{{
|
||||
$t('dynamicsForm.input_type_list.MultiSelect')
|
||||
}}
|
||||
</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'RadioCard'">{{
|
||||
$t('dynamicsForm.input_type_list.RadioCard')
|
||||
}}
|
||||
</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'DatePicker'">{{
|
||||
$t('dynamicsForm.input_type_list.DatePicker')
|
||||
}}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.required')">
|
||||
<template #default="{ row }">
|
||||
<div @click.stop>
|
||||
<el-switch disabled size="small" v-model="row.required"/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.operation')" align="left" width="90">
|
||||
<template #default="{ row, $index }">
|
||||
<span class="mr-4">
|
||||
<el-tooltip effect="dark" :content="$t('common.modify')" placement="top">
|
||||
<el-button type="primary" text @click.stop="openAddInitDialog(row, $index)">
|
||||
<el-icon><EditPen/></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip effect="dark" :content="$t('common.delete')" placement="top">
|
||||
<el-button type="primary" text @click="deleteInitField($index)">
|
||||
<el-icon>
|
||||
<Delete/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="flex-between">
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('common.param.inputParam') }}
|
||||
<el-text type="info" class="color-secondary">
|
||||
{{ $t('views.tool.form.param.paramInfo1') }}
|
||||
</el-text>
|
||||
</h4>
|
||||
<el-button link type="primary" @click="openAddDialog()">
|
||||
<el-icon class="mr-4">
|
||||
<Plus/>
|
||||
</el-icon>
|
||||
{{ $t('common.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table ref="inputFieldTableRef" :data="form.input_field_list" class="mb-16">
|
||||
<el-table-column prop="name" :label="$t('views.tool.form.paramName.label')"/>
|
||||
<el-table-column :label="$t('views.tool.form.dataType.label')">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info" class="info-tag">{{ row.type }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.required')">
|
||||
<template #default="{ row }">
|
||||
<div @click.stop>
|
||||
<el-switch size="small" v-model="row.is_required"/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="source" :label="$t('views.tool.form.source.label')">
|
||||
<template #default="{ row }">
|
||||
{{
|
||||
row.source === 'custom'
|
||||
? $t('views.tool.form.source.custom')
|
||||
: $t('views.tool.form.source.reference')
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.operation')" align="left" width="90">
|
||||
<template #default="{ row, $index }">
|
||||
<span class="mr-4">
|
||||
<el-tooltip effect="dark" :content="$t('common.modify')" placement="top">
|
||||
<el-button type="primary" text @click.stop="openAddDialog(row, $index)">
|
||||
<el-icon><EditPen/></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip effect="dark" :content="$t('common.delete')" placement="top">
|
||||
<el-button type="primary" text @click="deleteField($index)">
|
||||
<el-icon>
|
||||
<Delete/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('views.tool.form.param.code') }}
|
||||
<span style="color: red; margin-left: -10px">*</span>
|
||||
<el-text type="info" class="color-secondary">
|
||||
{{ $t('views.tool.form.param.paramInfo2') }}
|
||||
</el-text>
|
||||
</h4>
|
||||
|
||||
<div class="mb-8" v-if="showEditor">
|
||||
<CodemirrorEditor
|
||||
:title="$t('views.tool.form.param.code')"
|
||||
v-model="form.code"
|
||||
@submitDialog="submitCodemirrorEditor"
|
||||
/>
|
||||
</div>
|
||||
<h4 class="title-decoration-1 mb-16 mt-16">
|
||||
{{ $t('common.param.outputParam') }}
|
||||
<el-text type="info" class="color-secondary">
|
||||
{{ $t('views.tool.form.param.paramInfo1') }}
|
||||
</el-text>
|
||||
</h4>
|
||||
<div class="flex-between border-r-4 p-8-12 mb-8 layout-bg lighter">
|
||||
<span>{{ $t('common.result') }} {result}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button :loading="loading" @click="visible = false">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button :loading="loading" @click="openDebug">{{ $t('common.debug') }}</el-button>
|
||||
<el-button type="primary" @click="submit(FormRef)" :loading="loading">
|
||||
{{ isEdit ? $t('common.save') : $t('common.create') }}
|
||||
</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ToolDebugDrawer ref="ToolDebugDrawerRef"/>
|
||||
<FieldFormDialog ref="FieldFormDialogRef" @refresh="refreshFieldList"/>
|
||||
<UserFieldFormDialog ref="UserFieldFormDialogRef" @refresh="refreshInitFieldList"/>
|
||||
<EditAvatarDialog ref="EditAvatarDialogRef" @refresh="refreshTool"/>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, watch, nextTick} from 'vue'
|
||||
import FieldFormDialog from '@/views/tool-shared-system/component/FieldFormDialog.vue'
|
||||
import ToolDebugDrawer from './ToolDebugDrawer.vue'
|
||||
import UserFieldFormDialog from '@/views/tool-shared-system/component/UserFieldFormDialog.vue'
|
||||
import EditAvatarDialog from '@/views/tool-shared-system/component/EditAvatarDialog.vue'
|
||||
import type {toolData} from '@/api/type/tool'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import type {FormInstance} from 'element-plus'
|
||||
import {MsgSuccess, MsgConfirm} from '@/utils/message'
|
||||
import {cloneDeep} from 'lodash'
|
||||
import {PermissionType, PermissionDesc} from '@/enums/model'
|
||||
import {t} from '@/locales'
|
||||
import {isAppIcon} from '@/utils/common'
|
||||
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
const FieldFormDialogRef = ref()
|
||||
const ToolDebugDrawerRef = ref()
|
||||
const UserFieldFormDialogRef = ref()
|
||||
const EditAvatarDialogRef = ref()
|
||||
const initFieldTableRef = ref()
|
||||
const inputFieldTableRef = ref()
|
||||
|
||||
const FormRef = ref()
|
||||
|
||||
const isEdit = ref(false)
|
||||
const loading = ref(false)
|
||||
const visible = ref(false)
|
||||
const showEditor = ref(false)
|
||||
const currentIndex = ref<any>(null)
|
||||
const showEditIcon = ref(false)
|
||||
|
||||
const form = ref<toolData>({
|
||||
name: '',
|
||||
desc: '',
|
||||
code: '',
|
||||
icon: '',
|
||||
input_field_list: [],
|
||||
init_field_list: [],
|
||||
})
|
||||
|
||||
watch(visible, (bool) => {
|
||||
if (!bool) {
|
||||
isEdit.value = false
|
||||
showEditor.value = false
|
||||
currentIndex.value = null
|
||||
form.value = {
|
||||
name: '',
|
||||
desc: '',
|
||||
code: '',
|
||||
icon: '',
|
||||
input_field_list: [],
|
||||
init_field_list: [],
|
||||
}
|
||||
FormRef.value?.clearValidate()
|
||||
}
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.tool.form.toolName.requiredMessage'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
function submitCodemirrorEditor(val: string) {
|
||||
form.value.code = val
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (isEdit.value || !areAllValuesNonEmpty(form.value)) {
|
||||
visible.value = false
|
||||
} else {
|
||||
MsgConfirm(t('common.tip'), t('views.tool.tip.saveMessage'), {
|
||||
confirmButtonText: t('common.confirm'),
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
visible.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function areAllValuesNonEmpty(obj: any) {
|
||||
return Object.values(obj).some((value) => {
|
||||
return Array.isArray(value)
|
||||
? value.length !== 0
|
||||
: value !== null && value !== undefined && value !== ''
|
||||
})
|
||||
}
|
||||
|
||||
function openDebug() {
|
||||
ToolDebugDrawerRef.value.open(form.value)
|
||||
}
|
||||
|
||||
function deleteField(index: any) {
|
||||
form.value.input_field_list?.splice(index, 1)
|
||||
}
|
||||
|
||||
function openAddDialog(data?: any, index?: any) {
|
||||
if (typeof index !== 'undefined') {
|
||||
currentIndex.value = index
|
||||
}
|
||||
|
||||
FieldFormDialogRef.value.open(data)
|
||||
}
|
||||
|
||||
function refreshFieldList(data: any) {
|
||||
if (currentIndex.value !== null) {
|
||||
form.value.input_field_list?.splice(currentIndex.value, 1, data)
|
||||
} else {
|
||||
form.value.input_field_list?.push(data)
|
||||
}
|
||||
currentIndex.value = null
|
||||
}
|
||||
|
||||
function openAddInitDialog(data?: any, index?: any) {
|
||||
if (typeof index !== 'undefined') {
|
||||
currentIndex.value = index
|
||||
}
|
||||
|
||||
UserFieldFormDialogRef.value.open(data)
|
||||
}
|
||||
|
||||
function refreshInitFieldList(data: any) {
|
||||
if (currentIndex.value !== null) {
|
||||
form.value.init_field_list?.splice(currentIndex.value, 1, data)
|
||||
} else {
|
||||
form.value.init_field_list?.push(data)
|
||||
}
|
||||
currentIndex.value = null
|
||||
UserFieldFormDialogRef.value.close()
|
||||
}
|
||||
|
||||
function refreshTool(data: any) {
|
||||
form.value.icon = data
|
||||
}
|
||||
|
||||
function deleteInitField(index: any) {
|
||||
form.value.init_field_list?.splice(index, 1)
|
||||
}
|
||||
|
||||
function openEditAvatar() {
|
||||
EditAvatarDialogRef.value.open(form.value)
|
||||
}
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid: any) => {
|
||||
if (valid) {
|
||||
if (isEdit.value) {
|
||||
ToolApi.putTool(form.value?.id as string, form.value, loading).then((res) => {
|
||||
MsgSuccess(t('common.editSuccess'))
|
||||
emit('refresh', res.data)
|
||||
visible.value = false
|
||||
})
|
||||
} else {
|
||||
ToolApi.postTool(form.value, loading).then((res) => {
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
emit('refresh')
|
||||
visible.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const open = (data: any) => {
|
||||
if (data) {
|
||||
isEdit.value = data?.id ? true : false
|
||||
form.value = cloneDeep(data)
|
||||
}
|
||||
visible.value = true
|
||||
setTimeout(() => {
|
||||
showEditor.value = true
|
||||
}, 100)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.tool.form.toolName.name')"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
append-to-body
|
||||
width="450"
|
||||
>
|
||||
<el-form
|
||||
label-position="top"
|
||||
ref="fieldFormRef"
|
||||
:rules="rules"
|
||||
:model="form"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
<el-form-item prop="name">
|
||||
<el-input v-model="form.name" maxlength="64" show-word-limit></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit(fieldFormRef)" :loading="loading">
|
||||
{{ isEdit ? $t('common.save') : $t('common.add') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const fieldFormRef = ref()
|
||||
const loading = ref<boolean>(false)
|
||||
const isEdit = ref<boolean>(false)
|
||||
|
||||
const form = ref<any>({
|
||||
name: ''
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.tool.form.toolName.placeholder'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
form.value = {
|
||||
name: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const open = (row: any, edit: boolean) => {
|
||||
if (row) {
|
||||
form.value = cloneDeep(row)
|
||||
}
|
||||
isEdit.value = edit || false
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
emit('refresh', form.value, isEdit.value)
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="`Logo ${$t('common.setting')}`"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
width="550"
|
||||
>
|
||||
<el-radio-group v-model="radioType" class="radio-block mb-16">
|
||||
<el-radio value="default">
|
||||
<p>{{ $t('common.EditAvatarDialog.default') }}</p>
|
||||
<el-avatar class="avatar-green" shape="square" :size="32">
|
||||
<img src="@/assets/node/icon_tool.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
</el-radio>
|
||||
|
||||
<el-radio value="custom">
|
||||
<p>{{ $t('common.EditAvatarDialog.customizeUpload') }}</p>
|
||||
<div class="flex mt-8">
|
||||
<el-avatar
|
||||
v-if="fileURL"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
class="mr-16"
|
||||
>
|
||||
<img :src="fileURL" alt="" />
|
||||
</el-avatar>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept="image/jpeg, image/png, image/gif"
|
||||
:on-change="onChange"
|
||||
>
|
||||
<el-button icon="Upload" :disabled="radioType !== 'custom'"
|
||||
>{{ $t('common.EditAvatarDialog.upload') }}
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div class="el-upload__tip info mt-8">
|
||||
{{ $t('common.EditAvatarDialog.sizeTip') }}
|
||||
</div>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit" :loading="loading">
|
||||
{{ $t('common.save') }}</el-button
|
||||
>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { MsgError, MsgSuccess } from '@/utils/message'
|
||||
import { defaultIcon, isAppIcon } from '@/utils/common'
|
||||
import { t } from '@/locales'
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const iconFile = ref<any>(null)
|
||||
const fileURL = ref<any>(null)
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const loading = ref(false)
|
||||
const detail = ref<any>(null)
|
||||
const radioType = ref('default')
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
iconFile.value = null
|
||||
fileURL.value = null
|
||||
}
|
||||
})
|
||||
|
||||
const open = (data: any) => {
|
||||
radioType.value = isAppIcon(data.icon) ? 'custom' : 'default'
|
||||
fileURL.value = isAppIcon(data.icon) ? data.icon : null
|
||||
detail.value = cloneDeep(data)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const onChange = (file: any) => {
|
||||
//1、判断文件大小是否合法,文件限制不能大于10MB
|
||||
const isLimit = file?.size / 1024 / 1024 < 10
|
||||
if (!isLimit) {
|
||||
// @ts-ignore
|
||||
MsgError(t('common.EditAvatarDialog.fileSizeExceeded'))
|
||||
return false
|
||||
} else {
|
||||
iconFile.value = file
|
||||
fileURL.value = URL.createObjectURL(file.raw)
|
||||
}
|
||||
}
|
||||
|
||||
function submit() {
|
||||
if (radioType.value === 'default') {
|
||||
emit('refresh', '/ui/favicon.ico')
|
||||
dialogVisible.value = false
|
||||
} else if (radioType.value === 'custom' && iconFile.value) {
|
||||
const fd = new FormData()
|
||||
fd.append('file', iconFile.value.raw)
|
||||
ToolApi.putToolIcon(detail.value.id, fd, loading).then((res: any) => {
|
||||
emit('refresh', res.data)
|
||||
dialogVisible.value = false
|
||||
})
|
||||
} else {
|
||||
MsgError(t('common.EditAvatarDialog.uploadImagePrompt'))
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="isEdit ? $t('common.param.editParam') : $t('common.param.addParam')"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
label-position="top"
|
||||
ref="fieldFormRef"
|
||||
:rules="rules"
|
||||
:model="form"
|
||||
require-asterisk-position="right"
|
||||
>
|
||||
<el-form-item :label="$t('views.tool.form.paramName.label')" prop="name">
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
:placeholder="$t('views.tool.form.paramName.placeholder')"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
@blur="form.name = form.name.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.tool.form.dataType.label')">
|
||||
<el-select v-model="form.type">
|
||||
<el-option v-for="item in typeOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.tool.form.source.label')">
|
||||
<el-select v-model="form.source">
|
||||
<el-option :label="$t('views.tool.form.source.reference')" value="reference" />
|
||||
<el-option :label="$t('views.tool.form.source.custom')" value="custom" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('views.tool.form.required.label')" @click.prevent>
|
||||
<el-switch size="small" v-model="form.is_required"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit(fieldFormRef)" :loading="loading">
|
||||
{{ isEdit ? $t('common.save') : $t('common.add') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { t } from '@/locales'
|
||||
const typeOptions = ['string', 'int', 'dict', 'array', 'float']
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const fieldFormRef = ref()
|
||||
const loading = ref<boolean>(false)
|
||||
const isEdit = ref(false)
|
||||
|
||||
const form = ref<any>({
|
||||
name: '',
|
||||
type: typeOptions[0],
|
||||
source: 'reference',
|
||||
is_required: true,
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.tool.form.paramName.placeholder'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
form.value = {
|
||||
name: '',
|
||||
type: typeOptions[0],
|
||||
source: 'reference',
|
||||
is_required: true,
|
||||
}
|
||||
isEdit.value = false
|
||||
}
|
||||
})
|
||||
|
||||
const open = (row: any) => {
|
||||
if (row) {
|
||||
form.value = cloneDeep(row)
|
||||
isEdit.value = true
|
||||
}
|
||||
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
emit('refresh', form.value)
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<el-drawer v-model="debugVisible" size="60%" :append-to-body="true">
|
||||
<template #header>
|
||||
<h4>{{ $t('common.param.initParam') }}</h4>
|
||||
</template>
|
||||
<div>
|
||||
<div v-if="form.init_field_list?.length > 0">
|
||||
<DynamicsForm
|
||||
v-model="form.init_params"
|
||||
:model="form.init_params"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:render_data="form.init_field_list"
|
||||
ref="dynamicsFormRef"
|
||||
>
|
||||
</DynamicsForm>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button type="primary" @click="submit()" :loading="loading">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, watch} from 'vue'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||
import {MsgSuccess} from '@/utils/message'
|
||||
import {t} from '@/locales'
|
||||
import {cloneDeep} from 'lodash'
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const dynamicsFormRef = ref()
|
||||
const loading = ref(false)
|
||||
const debugVisible = ref(false)
|
||||
|
||||
const form = ref<any>({
|
||||
init_params: {},
|
||||
})
|
||||
|
||||
watch(debugVisible, (bool) => {
|
||||
if (!bool) {
|
||||
form.value = {
|
||||
init_params: {},
|
||||
is_active: false,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const submit = async () => {
|
||||
dynamicsFormRef.value.validate().then(() => {
|
||||
ToolApi.putTool(form.value?.id as string, form.value, loading).then((res) => {
|
||||
MsgSuccess(t('common.editSuccess'))
|
||||
emit('refresh')
|
||||
debugVisible.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const open = (data: any, is_active: boolean) => {
|
||||
if (data) {
|
||||
form.value = cloneDeep(data)
|
||||
form.value.is_active = is_active
|
||||
}
|
||||
const init_params = form.value.init_field_list
|
||||
.map((item: any) => {
|
||||
if (item.show_default_value === false) {
|
||||
return {[item.field]: undefined}
|
||||
}
|
||||
return {[item.field]: item.default_value}
|
||||
})
|
||||
.reduce((x: any, y: any) => ({...x, ...y}), {})
|
||||
form.value.init_params = {...init_params, ...form.value.init_params}
|
||||
debugVisible.value = true
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
})
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<template>
|
||||
<el-drawer v-model="visibleInternalDesc" size="60%" :append-to-body="true">
|
||||
<template #header>
|
||||
<div class="flex align-center" style="margin-left: -8px">
|
||||
<el-button class="cursor mr-4" link @click.prevent="visibleInternalDesc = false">
|
||||
<el-icon :size="20">
|
||||
<Back />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<h4>详情</h4>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<div class="card-header">
|
||||
<div class="flex-between">
|
||||
<div class="title flex align-center">
|
||||
<el-avatar
|
||||
v-if="isAppIcon(functionDetail?.icon)"
|
||||
shape="square"
|
||||
:size="64"
|
||||
style="background: none"
|
||||
class="mr-8"
|
||||
>
|
||||
<img :src="functionDetail?.icon" alt="" />
|
||||
</el-avatar>
|
||||
<el-avatar
|
||||
v-else-if="functionDetail?.name"
|
||||
:name="functionDetail?.name"
|
||||
pinyinColor
|
||||
shape="square"
|
||||
:size="64"
|
||||
class="mr-8"
|
||||
/>
|
||||
<div class="ml-16">
|
||||
<h3 class="mb-8">{{ functionDetail.name }}</h3>
|
||||
<el-text type="info" v-if="functionDetail?.desc">
|
||||
{{ functionDetail.desc }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
<div @click.stop>
|
||||
<el-button type="primary" @click="addInternalFunction(functionDetail)">
|
||||
{{ $t('common.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-16">
|
||||
<el-text type="info">
|
||||
<div>{{ $t('common.author') }}: MaxKB</div>
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
<MdPreview
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="markdownContent"
|
||||
style="background: none"
|
||||
noImgZoomIn
|
||||
/>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { isAppIcon } from '@/utils/common'
|
||||
const emit = defineEmits(['refresh', 'addFunction'])
|
||||
|
||||
const visibleInternalDesc = ref(false)
|
||||
const markdownContent = ref('')
|
||||
const functionDetail = ref<any>({})
|
||||
|
||||
watch(visibleInternalDesc, (bool) => {
|
||||
if (!bool) {
|
||||
markdownContent.value = ''
|
||||
}
|
||||
})
|
||||
|
||||
const open = (data: any, detail: any) => {
|
||||
functionDetail.value = detail
|
||||
if (data) {
|
||||
markdownContent.value = cloneDeep(data)
|
||||
}
|
||||
visibleInternalDesc.value = true
|
||||
}
|
||||
|
||||
const addInternalFunction = (data: any) => {
|
||||
emit('addFunction', data)
|
||||
visibleInternalDesc.value = false
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="isEdit ? $t('common.param.editParam') : $t('common.param.addParam')"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:before-close="close"
|
||||
append-to-body
|
||||
>
|
||||
<DynamicsFormConstructor
|
||||
v-model="currentRow"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:input_type_list="inputTypeList"
|
||||
ref="DynamicsFormConstructorRef"
|
||||
></DynamicsFormConstructor>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="close"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit()" :loading="loading">
|
||||
{{ isEdit ? $t('common.save') : $t('common.add') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import DynamicsFormConstructor from '@/components/dynamics-form/constructor/index.vue'
|
||||
import type { FormField } from '@/components/dynamics-form/type'
|
||||
import _ from 'lodash'
|
||||
import { t } from '@/locales'
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const DynamicsFormConstructorRef = ref()
|
||||
const loading = ref<boolean>(false)
|
||||
const isEdit = ref(false)
|
||||
const currentItem = ref<FormField | any>()
|
||||
const check_field = (field_list: Array<string>, obj: any) => {
|
||||
return field_list.every((field) => _.get(obj, field, undefined) !== undefined)
|
||||
}
|
||||
const currentRow = computed(() => {
|
||||
if (currentItem.value) {
|
||||
const row = currentItem.value
|
||||
switch (row.type) {
|
||||
case 'input':
|
||||
if (check_field(['field', 'input_type', 'label', 'required', 'attrs'], currentItem.value)) {
|
||||
return currentItem.value
|
||||
}
|
||||
return {
|
||||
attrs: row.attrs || { maxlength: 200, minlength: 0 },
|
||||
field: row.field || row.variable,
|
||||
input_type: 'TextInput',
|
||||
label: row.label || row.name,
|
||||
default_value: row.default_value,
|
||||
required: row.required != undefined ? row.required : row.is_required,
|
||||
}
|
||||
case 'select':
|
||||
if (
|
||||
check_field(
|
||||
['field', 'input_type', 'label', 'required', 'option_list'],
|
||||
currentItem.value,
|
||||
)
|
||||
) {
|
||||
return currentItem.value
|
||||
}
|
||||
return {
|
||||
attrs: row.attrs || {},
|
||||
field: row.field || row.variable,
|
||||
input_type: 'SingleSelect',
|
||||
label: row.label || row.name,
|
||||
default_value: row.default_value,
|
||||
required: row.required != undefined ? row.required : row.is_required,
|
||||
option_list: row.option_list
|
||||
? row.option_list
|
||||
: row.optionList.map((o: any) => {
|
||||
return { key: o, value: o }
|
||||
}),
|
||||
}
|
||||
|
||||
case 'date':
|
||||
if (
|
||||
check_field(
|
||||
[
|
||||
'field',
|
||||
'input_type',
|
||||
'label',
|
||||
'required',
|
||||
'attrs.format',
|
||||
'attrs.value-format',
|
||||
'attrs.type',
|
||||
],
|
||||
currentItem.value,
|
||||
)
|
||||
) {
|
||||
return currentItem.value
|
||||
}
|
||||
return {
|
||||
field: row.field || row.variable,
|
||||
input_type: 'DatePicker',
|
||||
label: row.label || row.name,
|
||||
default_value: row.default_value,
|
||||
required: row.required != undefined ? row.required : row.is_required,
|
||||
attrs: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
'value-format': 'YYYY-MM-DD HH:mm:ss',
|
||||
type: 'datetime',
|
||||
},
|
||||
}
|
||||
default:
|
||||
return currentItem.value
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
input_type: 'TextInput',
|
||||
required: false,
|
||||
attrs: { maxlength: 200, minlength: 0 },
|
||||
show_default_value: true,
|
||||
}
|
||||
}
|
||||
})
|
||||
const currentIndex = ref(null)
|
||||
const inputTypeList = ref([
|
||||
{ label: t('dynamicsForm.input_type_list.TextInput'), value: 'TextInputConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.PasswordInput'), value: 'PasswordInputConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.SingleSelect'), value: 'SingleSelectConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.MultiSelect'), value: 'MultiSelectConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.RadioCard'), value: 'RadioCardConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.DatePicker'), value: 'DatePickerConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.SwitchInput'), value: 'SwitchInputConstructor' },
|
||||
])
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
const open = (row: any, index: any) => {
|
||||
dialogVisible.value = true
|
||||
|
||||
if (row) {
|
||||
isEdit.value = true
|
||||
currentItem.value = cloneDeep(row)
|
||||
currentIndex.value = index
|
||||
} else {
|
||||
currentItem.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
isEdit.value = false
|
||||
currentIndex.value = null
|
||||
currentItem.value = null as any
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
const formEl = DynamicsFormConstructorRef.value
|
||||
if (!formEl) return
|
||||
await formEl.validate().then(() => {
|
||||
emit('refresh', formEl?.getData(), currentIndex.value)
|
||||
isEdit.value = false
|
||||
currentItem.value = null as any
|
||||
currentIndex.value = null
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,448 @@
|
|||
<template>
|
||||
<div class="tool-shared">
|
||||
<ContentContainer>
|
||||
<template #header>
|
||||
<div class="shared-header">
|
||||
<span class="title">{{ t('views.system.shared_resources') }}</span>
|
||||
<el-icon size="12">
|
||||
<rightOutlined></rightOutlined>
|
||||
</el-icon>
|
||||
<span class="sub-title">{{ t('views.tool.title') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #search>
|
||||
<div class="flex">
|
||||
<div class="flex-between complex-search">
|
||||
<el-select
|
||||
class="complex-search__left"
|
||||
v-model="search_type"
|
||||
style="width: 120px"
|
||||
@change="search_type_change"
|
||||
>
|
||||
<el-option :label="$t('common.creator')" value="create_user" />
|
||||
|
||||
<el-option :label="$t('views.model.modelForm.modeName.label')" value="name" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-if="search_type === 'name'"
|
||||
v-model="search_form.name"
|
||||
@change="getList"
|
||||
:placeholder="$t('common.searchBar.placeholder')"
|
||||
style="width: 220px"
|
||||
clearable
|
||||
/>
|
||||
<el-select
|
||||
v-else-if="search_type === 'create_user'"
|
||||
v-model="search_form.create_user"
|
||||
@change="getList"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option v-for="u in user_options" :key="u.id" :value="u.id" :label="u.username" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button class="ml-16" type="primary"> {{ $t('common.create') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<el-row v-if="toolList.length > 0" :gutter="15">
|
||||
<template v-for="(item, index) in toolList" :key="index">
|
||||
<el-col
|
||||
v-if="item.resource_type === 'folder'"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="12"
|
||||
:lg="8"
|
||||
:xl="6"
|
||||
class="mb-16"
|
||||
>
|
||||
<CardBox
|
||||
:title="item.name"
|
||||
:description="item.desc || $t('common.noData')"
|
||||
class="cursor"
|
||||
>
|
||||
<template #icon>
|
||||
<el-avatar shape="square" :size="32" style="background: none">
|
||||
<AppIcon iconName="app-folder" style="font-size: 32px"></AppIcon>
|
||||
</el-avatar>
|
||||
</template>
|
||||
<template #subTitle>
|
||||
<el-text class="color-secondary lighter" size="small">
|
||||
{{ $t('common.creator') }}: {{ item.username }}
|
||||
</el-text>
|
||||
</template>
|
||||
</CardBox>
|
||||
</el-col>
|
||||
<el-col v-else :xs="24" :sm="12" :md="12" :lg="8" :xl="6" class="mb-16">
|
||||
<CardBox isShared :title="item.name" :description="item.desc" class="cursor">
|
||||
<template #icon>
|
||||
<el-avatar
|
||||
v-if="isAppIcon(item?.icon)"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
class="mr-8"
|
||||
>
|
||||
<img :src="item?.icon" alt="" />
|
||||
</el-avatar>
|
||||
<el-avatar v-else class="avatar-green" shape="square" :size="32">
|
||||
<img src="@/assets/node/icon_tool.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
</template>
|
||||
<template #subTitle>
|
||||
<el-text class="color-secondary lighter" size="small">
|
||||
{{ $t('common.creator') }}: {{ item.username }}
|
||||
</el-text>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div v-if="item.is_active" class="flex align-center">
|
||||
<el-icon class="color-success mr-8" style="font-size: 16px">
|
||||
<SuccessFilled />
|
||||
</el-icon>
|
||||
<span class="color-secondary">
|
||||
{{ $t('common.status.enabled') }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-else class="flex align-center">
|
||||
<AppIcon iconName="app-disabled" class="color-secondary mr-8"></AppIcon>
|
||||
<span class="color-secondary">
|
||||
{{ $t('common.status.disabled') }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #mouseEnter>
|
||||
<div @click.stop>
|
||||
<el-switch
|
||||
v-model="item.is_active"
|
||||
:before-change="() => changeState(item)"
|
||||
size="small"
|
||||
class="mr-4"
|
||||
/>
|
||||
<el-divider direction="vertical" />
|
||||
<el-dropdown trigger="click">
|
||||
<el-button text @click.stop>
|
||||
<el-icon>
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
icon="Lock"
|
||||
@click.stop="openAuthorizedWorkspaceDialog(item)"
|
||||
>{{ $t('views.system.authorized_workspace') }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
v-if="!item.template_id"
|
||||
:disabled="!canEdit(item)"
|
||||
@click.stop="openCreateDialog(item)"
|
||||
>
|
||||
<el-icon>
|
||||
<EditPen />
|
||||
</el-icon>
|
||||
{{ $t('common.edit') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:disabled="!canEdit(item)"
|
||||
v-if="!item.template_id"
|
||||
@click.stop="copyTool(item)"
|
||||
>
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
{{ $t('common.copy') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="item.init_field_list?.length > 0"
|
||||
:disabled="!canEdit(item)"
|
||||
@click.stop="configInitParams(item)"
|
||||
>
|
||||
<AppIcon iconName="app-operation" class="mr-4"></AppIcon>
|
||||
{{ $t('common.param.initParam') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="!item.template_id"
|
||||
:disabled="!canEdit(item)"
|
||||
@click.stop="exportTool(item)"
|
||||
>
|
||||
<AppIcon iconName="app-export"></AppIcon>
|
||||
{{ $t('common.export') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:disabled="!canEdit(item)"
|
||||
divided
|
||||
@click.stop="deleteTool(item)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
{{ $t('common.delete') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</CardBox>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
<el-empty :description="$t('common.noData')" v-else />
|
||||
</div>
|
||||
</ContentContainer>
|
||||
<InitParamDrawer ref="InitParamDrawerRef" @refresh="refresh" />
|
||||
<ToolFormDrawer ref="ToolFormDrawerRef" @refresh="refresh" :title="ToolDrawertitle" />
|
||||
<AuthorizedWorkspace ref="AuthorizedWorkspaceDialogRef"></AuthorizedWorkspace>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, reactive, computed } from 'vue'
|
||||
import { cloneDeep, get } from 'lodash'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import useStore from '@/stores/modules-shared-system'
|
||||
import InitParamDrawer from '@/views/tool-shared-system/component/InitParamDrawer.vue'
|
||||
import ToolFormDrawer from './ToolFormDrawer.vue'
|
||||
import { t } from '@/locales'
|
||||
import { isAppIcon } from '@/utils/common'
|
||||
import iconMap from '@/components/app-icon/icons/common'
|
||||
import AuthorizedWorkspace from '@/views/knowledge-shared-system/AuthorizedWorkspace.vue'
|
||||
import { MsgSuccess, MsgConfirm, MsgError } from '@/utils/message'
|
||||
|
||||
const { folder, user } = useStore()
|
||||
const rightOutlined = iconMap['right-outlined'].iconReader()
|
||||
const InitParamDrawerRef = ref()
|
||||
const search_type = ref('name')
|
||||
const search_form = ref<{
|
||||
name: string
|
||||
create_user: string
|
||||
}>({
|
||||
name: '',
|
||||
create_user: '',
|
||||
})
|
||||
const user_options = ref<any[]>([])
|
||||
|
||||
const AuthorizedWorkspaceDialogRef = ref()
|
||||
function openAuthorizedWorkspaceDialog(row: any) {
|
||||
if (AuthorizedWorkspaceDialogRef.value) {
|
||||
AuthorizedWorkspaceDialogRef.value.open(row, 'Tool')
|
||||
}
|
||||
}
|
||||
const loading = ref(false)
|
||||
const changeStateloading = ref(false)
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 30,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const folderList = ref<any[]>([])
|
||||
const toolList = ref<any[]>([])
|
||||
const currentFolder = ref<any>({})
|
||||
|
||||
const search_type_change = () => {
|
||||
search_form.value = { name: '', create_user: '' }
|
||||
}
|
||||
const canEdit = (row: any) => {
|
||||
return user.userInfo?.id === row?.user_id
|
||||
}
|
||||
|
||||
const ToolFormDrawerRef = ref()
|
||||
const ToolDrawertitle = ref('')
|
||||
|
||||
function openCreateDialog(data?: any) {
|
||||
// 有template_id的不允许编辑,是模板转换来的
|
||||
if (data?.template_id) {
|
||||
return
|
||||
}
|
||||
ToolDrawertitle.value = data ? t('views.tool.editTool') : t('views.tool.createTool')
|
||||
if (data) {
|
||||
if (data?.permission_type !== 'PUBLIC' || canEdit(data)) {
|
||||
ToolApi.getToolById(data?.id, changeStateloading).then((res) => {
|
||||
ToolFormDrawerRef.value.open(res.data)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
ToolFormDrawerRef.value.open(data)
|
||||
}
|
||||
}
|
||||
|
||||
function getList() {
|
||||
const params = {
|
||||
folder_id: currentFolder.value?.id || 'root',
|
||||
scope: 'WORKSPACE',
|
||||
}
|
||||
ToolApi.getToolList(paginationConfig, params, loading).then((res) => {
|
||||
paginationConfig.total = res.data?.total
|
||||
toolList.value = [...toolList.value, ...res.data?.records]
|
||||
})
|
||||
}
|
||||
|
||||
function getFolder() {
|
||||
const params = {}
|
||||
folder.asyncGetFolder('TOOL', params, loading).then((res: any) => {
|
||||
folderList.value = res.data
|
||||
currentFolder.value = res.data?.[0] || {}
|
||||
getList()
|
||||
})
|
||||
}
|
||||
|
||||
async function changeState(row: any) {
|
||||
if (row.is_active) {
|
||||
MsgConfirm(
|
||||
`${t('views.tool.disabled.confirmTitle')}${row.name} ?`,
|
||||
t('views.tool.disabled.confirmMessage'),
|
||||
{
|
||||
confirmButtonText: t('common.status.disable'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
).then(() => {
|
||||
const obj = {
|
||||
is_active: !row.is_active,
|
||||
}
|
||||
ToolApi.putTool(row.id, obj, changeStateloading)
|
||||
.then(() => {
|
||||
return true
|
||||
})
|
||||
.catch(() => {
|
||||
return false
|
||||
})
|
||||
})
|
||||
} else {
|
||||
const res = await ToolApi.getToolById(row.id, changeStateloading)
|
||||
if (
|
||||
!res.data.init_params &&
|
||||
res.data.init_field_list &&
|
||||
res.data.init_field_list.length > 0 &&
|
||||
res.data.init_field_list.filter((item: any) => item.default_value && item.show_default_value)
|
||||
.length !== res.data.init_field_list.length
|
||||
) {
|
||||
InitParamDrawerRef.value.open(res.data, !row.is_active)
|
||||
return false
|
||||
}
|
||||
const obj = {
|
||||
is_active: !row.is_active,
|
||||
}
|
||||
ToolApi.putTool(row.id, obj, changeStateloading)
|
||||
.then(() => {
|
||||
return true
|
||||
})
|
||||
.catch(() => {
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function refresh(data: any) {
|
||||
if (data) {
|
||||
const index = toolList.value.findIndex((v) => v.id === data.id)
|
||||
// if (user.userInfo && data.user_id === user.userInfo.id) {
|
||||
// data.username = user.userInfo.username
|
||||
// } else {
|
||||
// data.username = userOptions.value.find((v) => v.value === data.user_id)?.label
|
||||
// }
|
||||
toolList.value.splice(index, 1, data)
|
||||
}
|
||||
paginationConfig.total = 0
|
||||
paginationConfig.current_page = 1
|
||||
toolList.value = []
|
||||
getList()
|
||||
}
|
||||
|
||||
function folderClickHandel(row: any) {
|
||||
currentFolder.value = row
|
||||
toolList.value = []
|
||||
getList()
|
||||
}
|
||||
|
||||
function copyTool(row: any) {
|
||||
ToolDrawertitle.value = t('views.tool.copyTool')
|
||||
const obj = cloneDeep(row)
|
||||
delete obj['id']
|
||||
obj['name'] = obj['name'] + ` ${t('views.tool.form.title.copy')}`
|
||||
ToolFormDrawerRef.value.open(obj)
|
||||
}
|
||||
|
||||
function exportTool(row: any) {
|
||||
ToolApi.exportTool(row.id, row.name, loading).catch((e: any) => {
|
||||
if (e.response.status !== 403) {
|
||||
e.response.data.text().then((res: string) => {
|
||||
MsgError(`${t('views.application.tip.ExportError')}:${JSON.parse(res).message}`)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function deleteTool(row: any) {
|
||||
MsgConfirm(
|
||||
`${t('views.tool.delete.confirmTitle')}${row.name} ?`,
|
||||
t('views.tool.delete.confirmMessage'),
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
ToolApi.delTool(row.id, loading).then(() => {
|
||||
const index = toolList.value.findIndex((v) => v.id === row.id)
|
||||
toolList.value.splice(index, 1)
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function configInitParams(item: any) {
|
||||
ToolApi.getToolById(item?.id, changeStateloading).then((res) => {
|
||||
InitParamDrawerRef.value.open(res.data)
|
||||
})
|
||||
}
|
||||
|
||||
// function importTool(file: any) {
|
||||
// const formData = new FormData()
|
||||
// formData.append('file', file.raw, file.name)
|
||||
// elUploadRef.value.clearFiles()
|
||||
// ToolApi
|
||||
// .postImportTool(formData, loading)
|
||||
// .then(async (res: any) => {
|
||||
// if (res?.data) {
|
||||
// searchHandle()
|
||||
// }
|
||||
// })
|
||||
// .catch((e: any) => {
|
||||
// if (e.code === 400) {
|
||||
// MsgConfirm(t('common.tip'), t('views.application.tip.professionalMessage'), {
|
||||
// cancelButtonText: t('common.confirm'),
|
||||
// confirmButtonText: t('common.professional')
|
||||
// }).then(() => {
|
||||
// window.open('https://maxkb.cn/pricing.html', '_blank')
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
getFolder()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tool-shared {
|
||||
padding-left: 8px;
|
||||
.shared-header {
|
||||
color: #646a73;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:deep(.el-icon i) {
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
color: #1f2329;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -110,7 +110,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import ToolApi from '@/api/tool/tool'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ import ToolDebugDrawer from './ToolDebugDrawer.vue'
|
|||
import UserFieldFormDialog from '@/views/tool/component/UserFieldFormDialog.vue'
|
||||
import EditAvatarDialog from '@/views/tool/component/EditAvatarDialog.vue'
|
||||
import type {toolData} from '@/api/type/tool'
|
||||
import ToolApi from '@/api/tool/tool'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import type {FormInstance} from 'element-plus'
|
||||
import {MsgSuccess, MsgConfirm} from '@/utils/message'
|
||||
import {cloneDeep} from 'lodash'
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import ToolApi from '@/api/tool/tool'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { MsgError, MsgSuccess } from '@/utils/message'
|
||||
import { defaultIcon, isAppIcon } from '@/utils/common'
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import {ref, watch} from 'vue'
|
||||
import ToolApi from '@/api/tool/tool'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||
import {MsgSuccess} from '@/utils/message'
|
||||
import {t} from '@/locales'
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref, reactive, computed } from 'vue'
|
||||
import { cloneDeep, get } from 'lodash'
|
||||
import ToolApi from '@/api/tool/tool'
|
||||
import ToolApi from '@/api/shared/tool'
|
||||
import useStore from '@/stores'
|
||||
import InitParamDrawer from '@/views/tool/component/InitParamDrawer.vue'
|
||||
import ToolFormDrawer from './ToolFormDrawer.vue'
|
||||
|
|
|
|||
Loading…
Reference in New Issue