feat: tool

This commit is contained in:
wangdan-fit2cloud 2025-06-24 14:49:09 +08:00
parent 2ab57802f4
commit 11fa3c4814
32 changed files with 930 additions and 620 deletions

View File

@ -0,0 +1,51 @@
<template>
<el-breadcrumb separator-icon="ArrowRight" style="line-height: 22px">
<h4 v-if="breadcrumbData?.length === 1">{{ breadcrumbData[0]?.name }}</h4>
<el-breadcrumb-item v-for="(item, index) in breadcrumbData" :key="index" v-else>
<h5 class="ml-4" v-if="index === breadcrumbData.length - 1">{{ item.name }}</h5>
<el-button v-else link @click="handleClick(item)">{{ item.name }}</el-button>
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { TreeToFlatten } from '@/utils/array'
defineOptions({ name: 'FolderBreadcrumb' })
import useStore from '@/stores'
const { folder, user } = useStore()
const props = defineProps({
folderList: {
type: Array,
default: () => [],
},
})
const breadcrumbData = computed(() => {
return folder.currentFolder?.id && getBreadcrumbData()
})
const emit = defineEmits(['click'])
function getBreadcrumbData() {
const targetId = folder.currentFolder?.id
const list = TreeToFlatten(props.folderList)
if (!folder.currentFolder) return [] // id
const breadcrumbList: any[] = []
let currentId: string | null = targetId
while (currentId) {
const currentNode = list.find((item: any) => item.id === currentId)
if (!currentNode) break //
breadcrumbList.unshift(currentNode) //
currentId = currentNode.parent_id //
}
return breadcrumbList
}
function handleClick(item: any) {
emit('click', item)
}
</script>
<style lang="scss" scoped></style>

View File

@ -52,6 +52,8 @@ import { ref, watch, reactive } from 'vue'
import folderApi from '@/api/folder'
import { MsgSuccess, MsgAlert } from '@/utils/message'
import { t } from '@/locales'
import useStore from '@/stores'
const { tool } = useStore()
const emit = defineEmits(['refresh'])
const props = defineProps({
@ -122,12 +124,14 @@ const submitHandle = async () => {
.then((res) => {
MsgSuccess(t('common.editSuccess'))
emit('refresh')
tool.setToolList([])
dialogVisible.value = false
})
} else {
folderApi.postFolder(sourceType.value, folderForm.value, loading).then((res) => {
MsgSuccess(t('common.createSuccess'))
emit('refresh')
tool.setToolList([])
dialogVisible.value = false
})
}

View File

@ -14,7 +14,7 @@
:class="currentNodeKey === 'share' && 'active'"
>
<AppIcon :iconName="iconName" style="font-size: 18px"></AppIcon>
<span class="ml-8 lighter">{{ $t(shareTitle) }}</span>
<span class="ml-8 lighter">{{ shareTitle }}</span>
</div>
<el-tree
ref="treeRef"
@ -37,7 +37,7 @@
</div>
<div
v-if="canOperation"
v-if="canOperation && node.level !== 3"
@click.stop
v-show="hoverNodeId === data.id"
@mouseenter.stop="handleMouseEnter(data)"
@ -61,7 +61,7 @@
<el-dropdown-item
divided
@click.stop="deleteFolder(data)"
:disabled="data.id === 'default'"
:disabled="!data.parent_id"
>
<el-icon><Delete /></el-icon>
{{ $t('common.delete') }}
@ -109,7 +109,7 @@ const props = defineProps({
},
shareTitle: {
type: String,
default: 'views.system.share_knowledge',
default: 'views.system.shared.shared_knowledge',
},
canOperation: {
type: Boolean,
@ -162,7 +162,7 @@ const handleNodeClick = (data: Tree) => {
const handleSharedNodeClick = () => {
treeRef.value?.setCurrentKey(undefined)
emit('handleNodeClick', { id: 'share', name: t(props.shareTitle) })
emit('handleNodeClick', { id: 'share', name: props.shareTitle })
}
function deleteFolder(row: Tree) {

View File

@ -25,6 +25,7 @@ import AiChat from './ai-chat/index.vue'
import KnowledgeIcon from './app-icon/KnowledgeIcon.vue'
import TagGroup from './tag-group/index.vue'
import WorkspaceDropdown from './workspace-dropdown/index.vue'
import FolderBreadcrumb from './folder-breadcrumb/index.vue'
export default {
install(app: App) {
app.component('LogoFull', LogoFull)
@ -53,5 +54,6 @@ export default {
app.component('KnowledgeIcon', KnowledgeIcon)
app.component('TagGroup', TagGroup)
app.component('WorkspaceDropdown', WorkspaceDropdown)
app.component('FolderBreadcrumb', FolderBreadcrumb)
},
}

View File

@ -1,10 +1,6 @@
export default {
title: 'System',
subTitle: 'Syetem Settings',
shared: 'Shared',
shared_resources: 'Shared Resources',
share_knowledge: 'Shared Knowledge',
authorized_workspace: 'Authorize Workspace',
test: 'Test Connection',
testSuccess: 'Successful',
testFailed: 'Test connection failed',
@ -23,7 +19,7 @@ export default {
ldap_filterPlaceholder: 'Please enter user filter',
ldap_mapping: 'LDAP Attribute Mapping',
ldap_mappingPlaceholder: 'Please enter LDAP attribute mapping',
enableAuthentication: 'Enable LDAP Authentication'
enableAuthentication: 'Enable LDAP Authentication',
},
cas: {
title: 'CAS',
@ -33,7 +29,7 @@ export default {
validateUrlPlaceholder: 'Please enter validation address',
redirectUrl: 'Callback Address',
redirectUrlPlaceholder: 'Please enter callback address',
enableAuthentication: 'Enable CAS Authentication'
enableAuthentication: 'Enable CAS Authentication',
},
oidc: {
title: 'OIDC',
@ -52,7 +48,7 @@ export default {
logoutEndpointPlaceholder: 'Please enter logout endpoint',
redirectUrl: 'Redirect URL',
redirectUrlPlaceholder: 'Please enter redirect URL',
enableAuthentication: 'Enable OIDC Authentication'
enableAuthentication: 'Enable OIDC Authentication',
},
oauth2: {
@ -73,7 +69,7 @@ export default {
redirectUrlPlaceholder: 'Please enter redirect URL',
filedMapping: 'Field Mapping',
filedMappingPlaceholder: 'Please enter field mapping',
enableAuthentication: 'Enable OAuth2 Authentication'
enableAuthentication: 'Enable OAuth2 Authentication',
},
scanTheQRCode: {
title: 'Scan the QR code',
@ -95,10 +91,10 @@ export default {
larkQrCode: 'Lark Scan Code Login',
dingtalkQrCode: 'DingTalk Scan Code Login',
setting: ' Setting',
access: 'Access'
}
access: 'Access',
},
},
theme: {
theme: {
title: 'Appearance Settings',
platformDisplayTheme: 'Platform Display Theme',
customTheme: 'Custom Theme',
@ -115,8 +111,10 @@ export default {
loginLogo: 'Login Logo',
websiteLogo: 'Website Logo',
replacePicture: 'Replace Picture',
websiteLogoTip: "The logo displayed on the top of the website. The recommended size is 48*48. It supports JPG, PNG, and GIF formats, with a size not exceeding 10MB.",
loginLogoTip: "Login page right-side logo. Recommended size is 204*52. Supports JPG, PNG, GIF formats, with a maximum file size of 10 MB.",
websiteLogoTip:
'The logo displayed on the top of the website. The recommended size is 48*48. It supports JPG, PNG, and GIF formats, with a size not exceeding 10MB.',
loginLogoTip:
'Login page right-side logo. Recommended size is 204*52. Supports JPG, PNG, GIF formats, with a maximum file size of 10 MB.',
loginBackgroundTip:
'Left background image, vector image recommended size 576 * 900, bitmap recommended size 1152 * 1800; Supports JPG, PNG, GIF, with a size not exceeding 10 MB.',
websiteName: 'Website Name',
@ -146,14 +144,14 @@ export default {
smtpPortPlaceholder: 'Please enter SMTP port',
smtpUser: 'SMTP User',
smtpUserPlaceholder: 'Please enter SMTP user',
sendEmail: 'Sender\'s Email',
sendEmailPlaceholder: 'Please enter the sender\'s email',
sendEmail: "Sender's Email",
sendEmailPlaceholder: "Please enter the sender's email",
smtpPassword: 'SMTP Password',
smtpPasswordPlaceholder: 'Please enter SMTP password',
enableSSL: 'Enable SSL (if the SMTP port is 465, you usually need to enable SSL)',
enableTLS: 'Enable TLS (if the SMTP port is 587, you usually need to enable TLS)'
enableTLS: 'Enable TLS (if the SMTP port is 587, you usually need to enable TLS)',
},
group: {
group: {
title: 'Team Member',
member: 'Member',
manage: 'Owner',
@ -166,7 +164,8 @@ export default {
delete: {
button: 'Remove',
confirmTitle: 'Wheather to remove the member:',
confirmMessage: "After removal, the member's knowledge base and application permissions will be revoked.",
confirmMessage:
"After removal, the member's knowledge base and application permissions will be revoked.",
},
setting: {
management: 'manegement',
@ -180,5 +179,16 @@ export default {
requiredMessage: 'Please enter Username/Email',
},
},
},
},
shared: {
label: 'Shared',
shared_resources: 'Shared Resources',
shared_tool: 'Shared Tool',
shared_model: 'Shared Model',
shared_knowledge: 'Shared Knowledge',
authorized_workspace: 'Authorize Workspace',
},
resource_management: {
label: 'Resource Management',
},
}

View File

@ -1,13 +1,6 @@
export default {
title: '系统管理',
subTitle: '系统设置',
shared: '共享',
shared_resources: '共享资源',
resource_management: '资源管理',
share_tool: '共享工具',
share_model: '共享模型',
share_knowledge: '共享知识库',
authorized_workspace: '授权工作空间',
test: '测试连接',
testSuccess: '测试连接成功',
testFailed: '测试连接失败',
@ -184,4 +177,15 @@ export default {
},
},
},
shared: {
label: '共享',
shared_resources: '共享资源',
shared_tool: '共享工具',
shared_model: '共享模型',
shared_knowledge: '共享知识库',
authorized_workspace: '授权工作空间',
},
resource_management: {
label: '资源管理',
},
}

View File

@ -110,4 +110,15 @@ export default {
enableSSL: '啟用 SSL如果 SMTP 端口是 465通常需要啟用 SSL',
enableTLS: '啟用 TLS如果 SMTP 端口是 587通常需要啟用 TLS',
},
shared: {
label: '共享',
shared_resources: '共享资源',
shared_tool: '共享工具',
shared_model: '共享模型',
shared_knowledge: '共享知识',
authorized_workspace: '授权工作区',
},
resource_management: {
label: '资源管理',
},
}

View File

@ -70,7 +70,7 @@ const systemRouter = {
meta: {
icon: 'app-folder-share',
iconActive: 'app-folder-share-active',
title: 'views.system.resource_management',
title: 'views.system.resource_management.label',
activeMenu: '/system',
parentPath: '/system',
parentName: 'system',
@ -119,7 +119,7 @@ const systemRouter = {
meta: {
icon: 'app-folder-share',
iconActive: 'app-folder-share-active',
title: 'views.system.shared_resources',
title: 'views.system.shared.shared_resources',
activeMenu: '/system',
parentPath: '/system',
parentName: 'system',
@ -146,7 +146,7 @@ const systemRouter = {
parentPath: '/system',
parentName: 'system',
},
component: () => import('@/views/shared/tool-shared/index.vue'),
component: () => import('@/views/system-shared/ToolSharedIndex.vue'),
},
{
path: '/system/shared/model',

View File

@ -12,6 +12,7 @@ import useDocumentStore from './modules/document'
import useApplicationStore from './modules/application'
import useChatLogStore from './modules/chat-log'
import useChatUserStore from './modules/chat-user'
import useToolStore from './modules/tool'
const useStore = () => ({
common: useCommonStore(),
login: useLoginStore(),
@ -27,6 +28,7 @@ const useStore = () => ({
application: useApplicationStore(),
chatLog: useChatLogStore(),
chatUser: useChatUserStore(),
tool: useToolStore(),
})
export default useStore

View File

@ -1,10 +1,15 @@
import {defineStore} from 'pinia'
import {type Ref} from 'vue'
import { defineStore } from 'pinia'
import { type Ref } from 'vue'
import folderApi from '@/api/folder'
const useFolderStore = defineStore('folder', {
state: () => ({}),
state: () => ({
currentFolder: {} as any,
}),
actions: {
setCurrentFolder(folder: any) {
this.currentFolder = folder
},
async asyncGetFolder(source: string, data: any, loading?: Ref<boolean>) {
return new Promise((resolve, reject) => {
folderApi

View File

@ -0,0 +1,42 @@
import { defineStore } from 'pinia'
import { type Ref } from 'vue'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
import type { pageRequest } from '@/api/type/common'
import useUserStore from './user'
import useFolderStore from './folder'
const useToolStore = defineStore('tool', {
state: () => ({
toolList: [] as any[],
}),
actions: {
setToolList(list: any[]) {
this.toolList = list
},
async asyncGetToolListPage(
page: pageRequest,
isShared?: boolean | undefined,
systemType: 'systemShare' | 'workspace' | 'systemManage' = 'workspace',
loading?: Ref<boolean>,
) {
return new Promise((resolve, reject) => {
const folder = useFolderStore()
const user = useUserStore()
const params = {
folder_id: folder.currentFolder?.id || user.getWorkspaceId(),
scope: systemType === 'systemShare' ? 'SHARED' : 'WORKSPACE',
}
loadSharedApi({ type: 'tool', isShared, systemType })
.getToolListPage(page, params, loading)
.then((res: any) => {
resolve(res)
})
.catch((error: any) => {
reject(error)
})
})
},
},
})
export default useToolStore

7
ui/src/utils/array.ts Normal file
View File

@ -0,0 +1,7 @@
// 树形结构转平
export function TreeToFlatten(treeData: any[]) {
return treeData.reduce((acc, node) => {
const { children, ...rest } = node
return [...acc, rest, ...(children ? TreeToFlatten(children) : [])]
}, [])
}

View File

@ -2,6 +2,9 @@ import knowledgeWorkspaceApi from '@/api/knowledge/knowledge'
import modelWorkspaceApi from '@/api/model/model'
import toolWorkspaceApi from '@/api/tool/tool'
import sharedWorkspaceApi from '@/api/shared-workspace'
import toolSystemShareApi from '@/api/system-shared/tool'
import modelSystemShareApi from '@/api/system-shared/model'
import knowledgeSystemShareApi from '@/api/system-shared/knowledge'
// 普通 API
const workspaceApiMap = {
@ -10,15 +13,41 @@ const workspaceApiMap = {
tool: toolWorkspaceApi,
} as any
/** API
* loadSharedApi('knowledge', true)
*/
// 系统分享 API
const systemShareApiMap = {
knowledge: knowledgeSystemShareApi,
model: modelSystemShareApi,
tool: toolSystemShareApi,
} as any
export function loadSharedApi(type: string, isShared?: boolean) {
// 资源管理 API
const systemManageApiMap = {
// knowledge: knowledgeWorkspaceApi,
// model: modelWorkspaceApi,
// tool: toolSystemShareApi,
} as any
const data = {
systemShare: systemShareApiMap,
workspace: workspaceApiMap,
systemManage: systemManageApiMap,
}
/** API
* loadSharedApi('knowledge', true,'systemShare')
*/
export function loadSharedApi({
type,
isShared,
systemType,
}: {
type: string
isShared?: boolean | undefined
systemType?: 'systemShare' | 'workspace' | 'systemManage'
}) {
if (isShared) {
// 共享 API
return sharedWorkspaceApi
} else {
return workspaceApiMap[type]
return data[systemType || 'workspace'][type]
}
}

View File

@ -3,7 +3,7 @@
<template #left>
<h4 class="p-16 pb-0">{{ $t('views.application.title') }}</h4>
<folder-tree
:source="FolderSource.TOOL"
:source="FolderSource.APPLICATION"
:data="folderList"
:currentNodeKey="currentFolder?.id"
@handleNodeClick="folderClickHandel"

View File

@ -199,10 +199,14 @@
router.push({ path: `/knowledge/${item.id}/${currentFolder.id}/document` })
"
v-hasPermission="[
RoleConst.ADMIN,
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
PermissionConst.KNOWLEDGE_DOCUMENT_READ.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_DOCUMENT_READ.getKnowledgeWorkspaceResourcePermission(item.id),]"
RoleConst.ADMIN,
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
PermissionConst.KNOWLEDGE_DOCUMENT_READ
.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_DOCUMENT_READ.getKnowledgeWorkspaceResourcePermission(
item.id,
),
]"
>
<template #icon>
<KnowledgeIcon :type="item.type" />
@ -214,7 +218,7 @@
</template>
<template #tag>
<el-tag v-if="isShared" type="info" class="info-tag">
{{ t('views.system.shared') }}
{{ t('views.system.shared.label') }}
</el-tag>
</template>
<template #footer>
@ -248,12 +252,21 @@
<el-dropdown-item
icon="Refresh"
@click.stop="syncKnowledge(item)"
v-if="item.type === 1 &&
hasPermission([
RoleConst.ADMIN,
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
PermissionConst.KNOWLEDGE_SYNC.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_SYNC.getKnowledgeWorkspaceResourcePermission(item.id)],'OR')"
v-if="
item.type === 1 &&
hasPermission(
[
RoleConst.ADMIN,
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
PermissionConst.KNOWLEDGE_SYNC
.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_SYNC.getKnowledgeWorkspaceResourcePermission(
item.id,
),
],
'OR',
)
"
>{{ $t('views.knowledge.setting.sync') }}
</el-dropdown-item>
<el-dropdown-item
@ -263,8 +276,11 @@
[
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
RoleConst.ADMIN,
PermissionConst.KNOWLEDGE_VECTOR.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_VECTOR.getKnowledgeWorkspaceResourcePermission(item.id),
PermissionConst.KNOWLEDGE_VECTOR
.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_VECTOR.getKnowledgeWorkspaceResourcePermission(
item.id,
),
],
'OR',
)
@ -282,8 +298,11 @@
[
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
RoleConst.ADMIN,
PermissionConst.KNOWLEDGE_PROBLEM_CREATE.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_PROBLEM_CREATE.getKnowledgeWorkspaceResourcePermission(item.id),
PermissionConst.KNOWLEDGE_PROBLEM_CREATE
.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_PROBLEM_CREATE.getKnowledgeWorkspaceResourcePermission(
item.id,
),
],
'OR',
)
@ -302,8 +321,11 @@
[
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
RoleConst.ADMIN,
PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_EDIT.getKnowledgeWorkspaceResourcePermission(item.id),
PermissionConst.KNOWLEDGE_EDIT
.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_EDIT.getKnowledgeWorkspaceResourcePermission(
item.id,
),
],
'OR',
)
@ -318,8 +340,11 @@
[
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
RoleConst.ADMIN,
PermissionConst.KNOWLEDGE_EXPORT.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_EXPORT.getKnowledgeWorkspaceResourcePermission(item.id),
PermissionConst.KNOWLEDGE_EXPORT
.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_EXPORT.getKnowledgeWorkspaceResourcePermission(
item.id,
),
],
'OR',
)
@ -335,8 +360,11 @@
[
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
RoleConst.ADMIN,
PermissionConst.KNOWLEDGE_EXPORT.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_EXPORT.getKnowledgeWorkspaceResourcePermission(item.id),
PermissionConst.KNOWLEDGE_EXPORT
.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_EXPORT.getKnowledgeWorkspaceResourcePermission(
item.id,
),
],
'OR',
)
@ -354,8 +382,11 @@
[
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
RoleConst.ADMIN,
PermissionConst.KNOWLEDGE_DELETE.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_DELETE.getKnowledgeWorkspaceResourcePermission(item.id),
PermissionConst.KNOWLEDGE_DELETE
.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.KNOWLEDGE_DELETE.getKnowledgeWorkspaceResourcePermission(
item.id,
),
],
'OR',
)
@ -476,7 +507,7 @@ function getList() {
[search_type.value]: search_form.value[search_type.value],
}
loadSharedApi('knowledge', isShared.value)
loadSharedApi({ type: 'knowledge', isShared: isShared.value })
.getKnowledgeListPage(paginationConfig, params, loading)
.then((res: any) => {
paginationConfig.total = res.data.total

View File

@ -29,7 +29,7 @@
</template>
<template #tag>
<el-tag v-if="isShared" type="info" class="info-tag">
{{ t('views.system.shared') }}
{{ t('views.system.shared.label') }}
</el-tag>
</template>
<ul>

View File

@ -9,7 +9,7 @@
:class="active?.provider === 'share' && 'active'"
>
<AppIcon iconName="app-folder-share-active" style="font-size: 18px"></AppIcon>
<span class="ml-8 lighter">{{ $t('views.system.share_model') }}</span>
<span class="ml-8 lighter">{{ $t('views.system.shared.shared_model') }}</span>
</div>
<div
class="all-mode flex cursor"
@ -145,7 +145,7 @@ const clickListHandle = (item: Provider) => {
}
const handleSharedNodeClick = () => {
emit('click', { provider: 'share', name: t('views.system.share_model') })
emit('click', { provider: 'share', name: t('views.system.shared.shared_model') })
}
</script>
<style lang="scss" scoped>

View File

@ -6,7 +6,7 @@
:data="provider_list"
@click="clickListHandle"
:loading="loading"
shareTitle="views.system.share_tool"
shareTitle="views.system.shared.shared_tool"
:showShared="permissionPrecise['is_share']()"
:active="active_provider"
/>
@ -55,8 +55,8 @@
</el-select>
</div>
<el-button
v-if="!isShared &&
permissionPrecise.addModel()
v-if="!isShared &&
permissionPrecise.addModel()
"
class="ml-16"
type="primary"
@ -202,7 +202,7 @@ const openCreateModel = (provider?: Provider, model_type?: string) => {
const list_model = () => {
const params = active_provider.value?.provider ? { provider: active_provider.value.provider } : {}
loadSharedApi('model', isShared.value)
loadSharedApi({ type: 'model', isShared: isShared.value })
.getModel({ ...model_search_form.value, ...params }, list_model_loading)
.then((ok: any) => {
model_list.value = ok.data

View File

@ -1,7 +1,7 @@
<template>
<div class="resource-manage_knowledge">
<div class="shared-header">
<span class="title">{{ t('views.system.resource_management') }}</span>
<span class="title">{{ t('views.system.resource_management.label') }}</span>
<el-icon size="12">
<rightOutlined></rightOutlined>
</el-icon>

View File

@ -1,7 +1,7 @@
<template>
<div class="resource-manage_tool">
<div class="shared-header">
<span class="title">{{ t('views.system.resource_management') }}</span>
<span class="title">{{ t('views.system.resource_management.label') }}</span>
<el-icon size="12">
<rightOutlined></rightOutlined>
</el-icon>

View File

@ -1,6 +1,6 @@
<template>
<div class="knowledge-shared">
<ContentContainer :header="$t('views.system.share_knowledge')">
<ContentContainer :header="$t('views.system.shared.shared_knowledge')">
<template #search>
<div class="flex">
<div class="flex-between complex-search">

View File

@ -3,7 +3,7 @@
<ContentContainer>
<template #header>
<div class="shared-header">
<span class="title">{{ t('views.system.shared_resources') }}</span>
<span class="title">{{ t('views.system.shared.shared_resources') }}</span>
<el-icon size="12">
<rightOutlined></rightOutlined>
</el-icon>
@ -230,7 +230,7 @@
<el-dropdown-item
icon="Lock"
@click.stop="openAuthorizedWorkspaceDialog(item)"
>{{ $t('views.system.authorized_workspace') }}</el-dropdown-item
>{{ $t('views.system.shared.authorized_workspace') }}</el-dropdown-item
>
<el-dropdown-item
icon="Setting"

View File

@ -1,7 +1,7 @@
<template>
<div class="model-shared">
<ContentContainer
:header="t('views.system.share_model')"
:header="t('views.system.shared.shared_model')"
v-loading="list_model_loading"
>
<template #search>

View File

@ -85,7 +85,7 @@
{{ $t('common.modify') }}
</el-dropdown-item>
<el-dropdown-item icon="Lock" @click.stop="openAuthorizedWorkspaceDialog(model)">{{
$t('views.system.authorized_workspace')
$t('views.system.shared.authorized_workspace')
}}</el-dropdown-item>
<el-dropdown-item

View File

@ -1,7 +1,7 @@
<template>
<div class="model-shared">
<div class="shared-header">
<span class="title">{{ t('views.system.shared_resources') }}</span>
<span class="title">{{ t('views.system.shared.shared_resources') }}</span>
<el-icon size="12">
<rightOutlined></rightOutlined>
</el-icon>

View File

@ -1,6 +1,6 @@
<template>
<div class="tool-shared">
<ContentContainer :header="$t('views.system.share_tool')">
<ContentContainer :header="$t('views.system.shared.shared_tool')">
<template #search>
<div class="flex">
<div class="flex-between complex-search">

View File

@ -3,7 +3,7 @@
<ContentContainer>
<template #header>
<div class="shared-header">
<span class="title">{{ t('views.system.shared_resources') }}</span>
<span class="title">{{ t('views.system.shared.shared_resources') }}</span>
<el-icon size="12">
<rightOutlined></rightOutlined>
</el-icon>
@ -132,7 +132,7 @@
<el-dropdown-item
icon="Lock"
@click.stop="openAuthorizedWorkspaceDialog(item)"
>{{ $t('views.system.authorized_workspace') }}</el-dropdown-item
>{{ $t('views.system.shared.authorized_workspace') }}</el-dropdown-item
>
<el-dropdown-item
v-if="!item.template_id"

View File

@ -2,7 +2,7 @@
<ContentContainer>
<template #header>
<div class="shared-header">
<span class="title">{{ t('views.system.shared_resources') }}</span>
<span class="title">{{ t('views.system.shared.shared_resources') }}</span>
<el-icon size="12">
<rightOutlined></rightOutlined>
</el-icon>
@ -17,7 +17,7 @@
:data="provider_list"
@click="clickListHandle"
:loading="loading"
shareTitle="views.system.share_tool"
:shareTitle="$t('views.system.shared.shared_tool')"
isShared
:active="active_provider"
/>

View File

@ -0,0 +1,46 @@
<template>
<div class="tool-shared">
<ToolListContainer>
<template #header>
<el-breadcrumb separator-icon="ArrowRight">
<el-breadcrumb-item>{{ t('views.system.shared.shared_resources') }}</el-breadcrumb-item>
<el-breadcrumb-item>
{{ t('views.tool.title') }}
</el-breadcrumb-item>
</el-breadcrumb>
</template>
</ToolListContainer>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive, computed } from 'vue'
import ToolListContainer from '@/views/tool/component/ToolListContainer.vue'
import { t } from '@/locales'
onMounted(() => {})
</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>

View File

@ -0,0 +1,583 @@
<template>
<ContentContainer>
<template #header>
<slot name="header"> </slot>
</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-dropdown trigger="click" v-if="!isShared">
<el-button type="primary" class="ml-8" v-if="permissionPrecise.create()">
{{ $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()">
<div class="flex align-center">
<el-avatar class="avatar-green" shape="square" :size="32">
<img src="@/assets/node/icon_tool.svg" style="width: 58%" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
<div class="lighter">空白创建</div>
</div>
</div>
</el-dropdown-item>
<el-upload
ref="elUploadRef"
:file-list="[]"
action="#"
multiple
:auto-upload="false"
:show-file-list="false"
:limit="1"
:on-change="(file: any, fileList: any) => importTool(file)"
class="import-button"
>
<el-dropdown-item class="w-full">
<div class="flex align-center w-full">
<el-avatar shape="square" class="mt-4" :size="36" style="background: none">
<img src="@/assets/icon_import.svg" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
<div class="lighter">{{ $t('common.importCreate') }}</div>
</div>
</div>
</el-dropdown-item>
</el-upload>
<el-dropdown-item @click="openToolStoreDialog()">
<div class="flex align-center">
<el-avatar class="avatar-green" shape="square" :size="32">
<img src="@/assets/node/icon_tool.svg" style="width: 58%" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
<div class="lighter">
{{ $t('views.tool.toolStore.createFromToolStore') }}
</div>
</div>
</div>
</el-dropdown-item>
<el-dropdown-item @click="openCreateFolder" divided>
<div class="flex align-center">
<AppIcon iconName="app-folder" style="font-size: 32px"></AppIcon>
<div class="pre-wrap ml-4">
<div class="lighter">
{{ $t('components.folder.addFolder') }}
</div>
</div>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<div
v-loading.fullscreen.lock="paginationConfig.current_page === 1 && loading"
style="max-height: calc(100vh - 140px)"
>
<InfiniteScroll
:size="tool.toolList.length"
:total="paginationConfig.total"
:page_size="paginationConfig.page_size"
v-model:current_page="paginationConfig.current_page"
@load="getList"
:loading="loading"
>
<el-row v-if="tool.toolList.length > 0" :gutter="15">
<template v-for="(item, index) in tool.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"
@click="clickFolder(item)"
>
<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.nick_name }}
</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"
class="cursor"
@click.stop="openCreateDialog(item)"
:disabled="
hasPermission(
[
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
RoleConst.USER.getWorkspaceRole,
PermissionConst.TOOL_EDIT.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.TOOL_EDIT.getWorkspacePermission,
],
'OR',
)
"
>
<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.nick_name }}
</el-text>
</template>
<template #tag>
<el-tag v-if="isShared" type="info" class="info-tag">
{{ t('views.system.shared.label') }}
</el-tag>
</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 v-if="!isShared">
<el-switch
v-model="item.is_active"
:before-change="() => changeState(item)"
size="small"
class="mr-4"
v-if="permissionPrecise.switch()"
/>
<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
v-if="item.template_id"
@click.stop="addInternalFunction(item, true)"
>
<el-icon>
<EditPen />
</el-icon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item
v-if="!item.template_id && permissionPrecise.edit()"
@click.stop="openCreateDialog(item)"
>
<el-icon>
<EditPen />
</el-icon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item
v-if="!item.template_id && permissionPrecise.copy()"
@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"
@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 && permissionPrecise.export()"
@click.stop="exportTool(item)"
>
<AppIcon iconName="app-export"></AppIcon>
{{ $t('common.export') }}
</el-dropdown-item>
<el-dropdown-item
v-if="permissionPrecise.delete()"
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 />
</InfiniteScroll>
</div>
</ContentContainer>
<InitParamDrawer ref="InitParamDrawerRef" @refresh="refresh" />
<ToolFormDrawer ref="ToolFormDrawerRef" @refresh="refresh" :title="ToolDrawertitle" />
<CreateFolderDialog ref="CreateFolderDialogRef" v-if="!isShared" />
<ToolStoreDialog ref="toolStoreDialogRef" @refresh="refresh" />
<AddInternalFunctionDialog
ref="addInternalFunctionDialogRef"
@refresh="confirmAddInternalFunction"
/>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive, computed, watch } from 'vue'
import { cloneDeep, get } from 'lodash'
import ToolApi from '@/api/tool/tool'
import { useRoute } from 'vue-router'
import InitParamDrawer from '@/views/tool/component/InitParamDrawer.vue'
import ToolFormDrawer from '@/views/tool/ToolFormDrawer.vue'
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
import { isAppIcon } from '@/utils/common'
import { MsgSuccess, MsgConfirm, MsgError } from '@/utils/message'
import { EditionConst, PermissionConst, RoleConst } from '@/utils/permission/data'
import { hasPermission } from '@/utils/permission/index'
import { FolderSource } from '@/enums/common'
import ToolStoreDialog from '@/views/tool/component/ToolStoreDialog.vue'
import AddInternalFunctionDialog from '@/views/tool/component/AddInternalFunctionDialog.vue'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
import permissionMap from '@/permission'
import useStore from '@/stores'
import { t } from '@/locales'
const route = useRoute()
const { folder, user, tool } = useStore()
const type = computed(() => {
if (route.path.includes('shared')) {
return 'systemShare'
} else if (route.path.includes('resource-management')) {
return 'systemManage'
} else {
return 'workspace'
}
})
const permissionPrecise = computed(() => {
return permissionMap['tool'][type.value]
})
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 loading = ref(false)
const changeStateloading = ref(false)
const paginationConfig = reactive({
current_page: 1,
page_size: 30,
total: 0,
})
const currentFolder = ref<any>({})
const isShared = computed(() => {
return folder.currentFolder.id === 'share'
})
const search_type_change = () => {
search_form.value = { name: '', create_user: '' }
}
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) {
loadSharedApi({ type: 'tool', systemType: type.value })
.getToolById(data?.id, loading)
.then((res) => {
ToolFormDrawerRef.value.open(res.data)
})
} else {
ToolFormDrawerRef.value.open(data)
}
}
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(() => {
const list = cloneDeep(tool.toolList)
const index = list.findIndex((v) => v.id === row.id)
list[index].is_active = !row.is_active
tool.setToolList(list)
return true
})
.catch(() => {
return false
})
})
} else {
const res = await loadSharedApi({ type: 'tool', systemType: type.value }).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,
}
loadSharedApi({ type: 'tool', systemType: type.value })
.putTool(row.id, obj, changeStateloading)
.then(() => {
const list = cloneDeep(tool.toolList)
const index = list.findIndex((v) => v.id === row.id)
list[index].is_active = !row.is_active
tool.setToolList(list)
return true
})
.catch(() => {
return false
})
}
}
function refresh(data?: any) {
if (data) {
const list = cloneDeep(tool.toolList)
const index = list.findIndex((v) => v.id === data.id)
list.splice(index, 1, data)
tool.setToolList(list)
} else {
paginationConfig.total = 0
paginationConfig.current_page = 1
tool.setToolList([])
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) {
loadSharedApi({ type: 'tool', systemType: type.value })
.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(() => {
loadSharedApi({ type: 'tool', systemType: type.value })
.delTool(row.id, loading)
.then(() => {
const list = cloneDeep(tool.toolList)
const index = list.findIndex((v) => v.id === row.id)
list.splice(index, 1)
tool.setToolList(list)
MsgSuccess(t('common.deleteSuccess'))
})
})
.catch(() => {})
}
function configInitParams(item: any) {
loadSharedApi({ type: 'tool', systemType: type.value })
.getToolById(item?.id, changeStateloading)
.then((res) => {
InitParamDrawerRef.value.open(res.data)
})
}
const toolStoreDialogRef = ref<InstanceType<typeof ToolStoreDialog>>()
function openToolStoreDialog() {
toolStoreDialogRef.value?.open(currentFolder.value.id)
}
const addInternalFunctionDialogRef = ref<InstanceType<typeof AddInternalFunctionDialog>>()
function addInternalFunction(data?: any, isEdit?: boolean) {
addInternalFunctionDialogRef.value?.open(data, isEdit)
}
function confirmAddInternalFunction(data?: any, isEdit?: boolean) {
if (isEdit) {
loadSharedApi({ type: 'tool', systemType: type.value })
.putTool(data?.id as string, { name: data.name }, loading)
.then((res) => {
MsgSuccess(t('common.saveSuccess'))
refresh()
})
}
}
const elUploadRef = ref()
function importTool(file: any) {
const formData = new FormData()
formData.append('file', file.raw, file.name)
elUploadRef.value.clearFiles()
loadSharedApi({ type: 'tool', systemType: type.value })
.postImportTool(formData, loading)
.then(async (res: any) => {
if (res?.data) {
tool.setToolList([])
getList()
}
})
.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')
})
}
})
}
//
const CreateFolderDialogRef = ref()
function openCreateFolder() {
CreateFolderDialogRef.value.open(FolderSource.TOOL, folder.currentFolder.id)
}
watch(
() => folder.currentFolder,
(newValue) => {
if (newValue && newValue.id) {
tool.setToolList([])
getList()
}
},
{ deep: true, immediate: true },
)
function getList() {
tool
.asyncGetToolListPage(paginationConfig, isShared.value, type.value, loading)
.then((res: any) => {
paginationConfig.total = res.data?.total
tool.setToolList([...tool.toolList, ...res.data?.records])
})
}
function clickFolder(item: any) {
folder.setCurrentFolder(item)
}
onMounted(() => {
if (type.value !== 'workspace') {
getList()
}
})
</script>
<style lang="scss" scoped></style>

View File

@ -8,321 +8,23 @@
:currentNodeKey="currentFolder?.id"
@handleNodeClick="folderClickHandel"
@refreshTree="refreshFolder"
:shareTitle="$t('views.system.share_tool')"
:shareTitle="$t('views.system.shared.shared_tool')"
:showShared="permissionPrecise['is_share']()"
class="p-8"
/>
</template>
<ContentContainer :header="currentFolder?.name">
<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-dropdown trigger="click" v-if="!isShared">
<el-button
type="primary"
class="ml-8"
v-if="permissionPrecise.create()"
>
{{ $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()">
<div class="flex align-center">
<el-avatar class="avatar-green" shape="square" :size="32">
<img src="@/assets/node/icon_tool.svg" style="width: 58%" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
<div class="lighter">空白创建</div>
</div>
</div>
</el-dropdown-item>
<el-upload
ref="elUploadRef"
:file-list="[]"
action="#"
multiple
:auto-upload="false"
:show-file-list="false"
:limit="1"
:on-change="(file: any, fileList: any) => importTool(file)"
class="import-button"
>
<el-dropdown-item class="w-full">
<div class="flex align-center w-full">
<el-avatar shape="square" class="mt-4" :size="36" style="background: none">
<img src="@/assets/icon_import.svg" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
<div class="lighter">{{ $t('common.importCreate') }}</div>
</div>
</div>
</el-dropdown-item>
</el-upload>
<el-dropdown-item @click="openToolStoreDialog()">
<div class="flex align-center">
<el-avatar class="avatar-green" shape="square" :size="32">
<img src="@/assets/node/icon_tool.svg" style="width: 58%" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
<div class="lighter">
{{ $t('views.tool.toolStore.createFromToolStore') }}
</div>
</div>
</div>
</el-dropdown-item>
<el-dropdown-item @click="openCreateFolder" divided>
<div class="flex align-center">
<AppIcon iconName="app-folder" style="font-size: 32px"></AppIcon>
<div class="pre-wrap ml-4">
<div class="lighter">
{{ $t('components.folder.addFolder') }}
</div>
</div>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<ToolListContainer>
<template #header>
<FolderBreadcrumb :folderList="folderList" @click="folderClickHandel" />
</template>
<div
v-loading.fullscreen.lock="paginationConfig.current_page === 1 && loading"
style="max-height: calc(100vh - 140px)"
>
<InfiniteScroll
:size="toolList.length"
:total="paginationConfig.total"
:page_size="paginationConfig.page_size"
v-model:current_page="paginationConfig.current_page"
@load="getList"
:loading="loading"
>
<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"
@click="clickFolder(item)"
>
<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.nick_name }}
</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"
class="cursor"
@click.stop="openCreateDialog(item)"
:disabled="
hasPermission(
[
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
RoleConst.USER.getWorkspaceRole,
PermissionConst.TOOL_EDIT.getWorkspacePermissionWorkspaceManageRole,
PermissionConst.TOOL_EDIT.getWorkspacePermission,
],
'OR',
)
"
>
<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.nick_name }}
</el-text>
</template>
<template #tag>
<el-tag v-if="isShared" type="info" class="info-tag">
{{ t('views.system.shared') }}
</el-tag>
</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 v-if="!isShared">
<el-switch
v-model="item.is_active"
:before-change="() => changeState(item)"
size="small"
class="mr-4"
v-if="permissionPrecise.switch()"
/>
<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 v-if="item.template_id" @click.stop="addInternalFunction(item, true)">
<el-icon>
<EditPen />
</el-icon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item
v-if="
!item.template_id &&
permissionPrecise.edit()
"
@click.stop="openCreateDialog(item)"
>
<el-icon>
<EditPen />
</el-icon>
{{ $t('common.edit') }}
</el-dropdown-item>
<el-dropdown-item
v-if="
!item.template_id &&
permissionPrecise.copy()
"
@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"
@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 &&
permissionPrecise.export()
"
@click.stop="exportTool(item)"
>
<AppIcon iconName="app-export"></AppIcon>
{{ $t('common.export') }}
</el-dropdown-item>
<el-dropdown-item
v-if="permissionPrecise.delete()"
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 />
</InfiniteScroll>
</div>
</ContentContainer>
<InitParamDrawer ref="InitParamDrawerRef" @refresh="refresh" />
<ToolFormDrawer ref="ToolFormDrawerRef" @refresh="refresh" :title="ToolDrawertitle" />
<CreateFolderDialog ref="CreateFolderDialogRef" @refresh="refreshFolder" v-if="!isShared" />
<ToolStoreDialog ref="toolStoreDialogRef" @refresh="refresh" />
<AddInternalFunctionDialog ref="addInternalFunctionDialogRef" @refresh="confirmAddInternalFunction" />
</ToolListContainer>
</LayoutContainer>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive, computed } from 'vue'
import { cloneDeep, get } from 'lodash'
import ToolApi from '@/api/tool/tool'
import useStore from '@/stores'
import InitParamDrawer from '@/views/tool/component/InitParamDrawer.vue'
import ToolFormDrawer from './ToolFormDrawer.vue'
import ToolListContainer from '@/views/tool/component/ToolListContainer.vue'
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
import { t } from '@/locales'
import { isAppIcon } from '@/utils/common'
import { MsgSuccess, MsgConfirm, MsgError } from '@/utils/message'
import { EditionConst, PermissionConst, RoleConst } from '@/utils/permission/data'
import { hasPermission } from '@/utils/permission/index'
import { FolderSource } from '@/enums/common'
import { ComplexPermission } from '@/utils/permission/type'
import ToolStoreDialog from './toolStore/ToolStoreDialog.vue'
@ -330,8 +32,9 @@ import AddInternalFunctionDialog from './toolStore/AddInternalFunctionDialog.vue
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
import permissionMap from '@/permission'
import { useRoute } from 'vue-router'
import useStore from '@/stores'
const route = useRoute()
const { folder, user } = useStore()
const { folder, tool } = useStore()
const type = computed(() => {
if (route.path.includes('shared')) {
@ -346,231 +49,11 @@ const permissionPrecise = computed(() => {
return permissionMap['tool'][type.value]
})
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 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 isShared = computed(() => {
return currentFolder.value.id === 'share'
})
const search_type_change = () => {
search_form.value = { name: '', create_user: '' }
}
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) {
ToolApi.getToolById(data?.id, loading).then((res) => {
ToolFormDrawerRef.value.open(res.data)
})
} else {
ToolFormDrawerRef.value.open(data)
}
}
function getList() {
const params = {
folder_id: currentFolder.value?.id || user.getWorkspaceId(),
scope: 'WORKSPACE',
}
loadSharedApi('tool', isShared.value)
.getToolListPage(paginationConfig, params, loading)
.then((res: any) => {
paginationConfig.total = res.data?.total
toolList.value = [...toolList.value, ...res.data?.records]
})
}
function clickFolder(item: any) {
currentFolder.value.id = item.id
toolList.value = []
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(() => {
const index = toolList.value.findIndex((v) => v.id === row.id)
toolList.value[index].is_active = !row.is_active
return true
})
.catch(() => {
return false
})
})
} else {
const res = await ToolApi.getToolById(row.id, changeStateloading)
if (
(!res.data.init_params || Object.keys(res.data.init_params).length === 0) &&
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(() => {
const index = toolList.value.findIndex((v) => v.id === row.id)
toolList.value[index].is_active = !row.is_active
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()
}
async function copyTool(row: any) {
ToolDrawertitle.value = t('views.tool.copyTool')
const res = await ToolApi.getToolById(row.id, changeStateloading)
const obj = cloneDeep(res.data)
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)
})
}
const toolStoreDialogRef = ref<InstanceType<typeof ToolStoreDialog>>()
function openToolStoreDialog() {
toolStoreDialogRef.value?.open(currentFolder.value.id)
}
const addInternalFunctionDialogRef = ref<InstanceType<typeof AddInternalFunctionDialog>>()
function addInternalFunction(data?: any, isEdit?: boolean) {
addInternalFunctionDialogRef.value?.open(data, isEdit)
}
function confirmAddInternalFunction(data?: any, isEdit?: boolean) {
if (isEdit) {
ToolApi.putTool(data?.id as string, { name: data.name }, loading).then((res) => {
MsgSuccess(t('common.saveSuccess'))
refresh()
})
}
}
const elUploadRef = ref()
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) {
toolList.value = []
getList()
}
})
.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')
})
}
})
}
//
const CreateFolderDialogRef = ref()
function openCreateFolder() {
CreateFolderDialogRef.value.open(FolderSource.TOOL, currentFolder.value.id)
}
function getFolder(bool?: boolean) {
const params = {}
folder.asyncGetFolder(FolderSource.TOOL, params, loading).then((res: any) => {
@ -578,19 +61,19 @@ function getFolder(bool?: boolean) {
if (bool) {
//
currentFolder.value = res.data?.[0] || {}
folder.setCurrentFolder(currentFolder.value)
}
getList()
})
}
function refreshFolder() {
toolList.value = []
getFolder()
}
function folderClickHandel(row: any) {
currentFolder.value = row
toolList.value = []
getList()
folder.setCurrentFolder(currentFolder.value)
tool.setToolList([])
}
function refreshFolder() {
getFolder()
}
onMounted(() => {

View File

@ -17,7 +17,7 @@ export default defineConfig(({ mode }) => {
const prefix = process.env.VITE_DYNAMIC_PREFIX || ENV.VITE_BASE_PATH
const proxyConf: Record<string, string | ProxyOptions> = {}
proxyConf['/api'] = {
// target: 'http://43.166.1.146:8080',
// target: 'http://47.92.195.88:8080',
target: 'http://127.0.0.1:8080',
changeOrigin: true,
rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'),