Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},
"dependencies": {
"@antv/layout": "^2.0.0",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-python": "^6.2.1",
"@codemirror/lint": "^6.9.7",
"@codemirror/state": "^6.7.1",
Expand Down
9 changes: 3 additions & 6 deletions ui/src/api/admin/system/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { get } from '../core/request'
import type { RequestParams, SystemUserOption } from '@/api/types'
import type { RequestParams, SystemUserOption, WorkspaceMemberOption } from '@/api/types'

/** 获取默认密码。 */
const getDefaultPassword = () => {
Expand All @@ -11,12 +11,9 @@ const getAllUsers = (query?: RequestParams) => {
return get<SystemUserOption[]>('/user/list', query)
}

/** 系统中选择空间下的成员用户
* @param workspaceId 工作空间ID
* 资源授权 和用户组的 用户下拉列表使用
*/
/** 获取指定工作空间的成员选项。 */
const getWorkspaceMembers = (workspaceId: string, query?: RequestParams) => {
return get<SystemUserOption[]>(`/workspace/${workspaceId}/user_member`, query)
return get<WorkspaceMemberOption[]>(`/workspace/${workspaceId}/user_member`, query)
}

export default {
Expand Down
38 changes: 38 additions & 0 deletions ui/src/api/admin/system/resource-authorization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { get, put } from '../core/request'
import type {
RequestParams,
ResourceAuthorizationType,
ResourcePermissionItem,
ResourcePermissionPayload,
} from '@/api/types'

const prefix = (workspaceId: string, userId: string, resource: ResourceAuthorizationType) =>
`/workspace/${workspaceId}/user_resource_permission/user/${userId}/resource/${resource}`

/** 获取工作空间成员对指定类型资源的权限列表。 */
const getUserResourcePermissions = (
workspaceId: string,
userId: string,
resource: ResourceAuthorizationType,
query?: RequestParams,
) => {
return get<ResourcePermissionItem[]>(prefix(workspaceId, userId, resource), query)
}

/** 更新工作空间成员对指定类型资源的权限。 */
const putUserResourcePermissions = (
workspaceId: string,
userId: string,
resource: ResourceAuthorizationType,
permissions: ResourcePermissionPayload[],
) => {
return put<ResourcePermissionPayload[], ResourcePermissionPayload[]>(
prefix(workspaceId, userId, resource),
permissions,
)
}

export default {
getUserResourcePermissions,
putUserResourcePermissions,
}
6 changes: 0 additions & 6 deletions ui/src/api/enums/folder.ts

This file was deleted.

2 changes: 1 addition & 1 deletion ui/src/api/enums/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** API 枚举值的唯一公共入口。 */
export * from './application'
export * from './folder'
export * from './login'
export * from './system-role'
export * from './model'
export * from './resource-authorization'
export * from './tool'
15 changes: 15 additions & 0 deletions ui/src/api/enums/resource-authorization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** 后端资源授权的资源类型。 */
export const RESOURCE_TYPE = {
APPLICATION: 'APPLICATION',
KNOWLEDGE: 'KNOWLEDGE',
MODEL: 'MODEL',
TOOL: 'TOOL',
} as const

/** 后端资源授权的权限值。 */
export const RESOURCE_PERMISSION = {
MANAGE: 'MANAGE',
NOT_AUTH: 'NOT_AUTH',
ROLE: 'ROLE',
VIEW: 'VIEW',
} as const
7 changes: 6 additions & 1 deletion ui/src/api/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ export interface OptionItem<Value extends boolean | number | string = string | n
[key: string]: unknown
}


export interface ExportError {
response: {
status: number
data: Blob
}
}

export interface WorkspaceMemberOption {
id: string
nick_name: string
roles?: string[]
}
6 changes: 4 additions & 2 deletions ui/src/api/types/folder.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/** Workspace 下多个资源模块共用的文件夹业务类型。 */

import { FOLDER_SOURCE } from '@/api/enums'
import { RESOURCE_TYPE } from '@/api/enums'
import type { ResourceType } from './resource-authorization'

export type FolderSource = (typeof FOLDER_SOURCE)[keyof typeof FOLDER_SOURCE]
/** 文件夹接口支持的资源类型,不包含没有文件夹层级的模型。 */
export type FolderSource = Exclude<ResourceType, typeof RESOURCE_TYPE.MODEL>

export interface FolderItem {
children?: FolderItem[]
Expand Down
1 change: 1 addition & 0 deletions ui/src/api/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export type * from './system-operate-log'
export type * from './system-role'
export type * from './system-email'
export type * from './model'
export type * from './resource-authorization'
export type * from './folder'
export type * from './tool'
27 changes: 27 additions & 0 deletions ui/src/api/types/resource-authorization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/** 系统资源授权 API 与页面共用的业务类型。 */

import { RESOURCE_TYPE, RESOURCE_PERMISSION } from '@/api/enums'
import type { ToolType } from './tool'

export type ResourceType = (typeof RESOURCE_TYPE)[keyof typeof RESOURCE_TYPE]
export type ResourceAuthorizationType = ResourceType
export type ResourcePermission = (typeof RESOURCE_PERMISSION)[keyof typeof RESOURCE_PERMISSION]

export interface ResourcePermissionItem {
auth_target_type: ResourceAuthorizationType
children?: ResourcePermissionItem[]
folder_id: string | null
icon?: string | null
id: string
name: string
permission: ResourcePermission
resource_type: 'application' | 'folder' | 'knowledge' | 'model' | 'tool'
tool_type?: ToolType | null
user_id: string
workspace_id: string
}

export interface ResourcePermissionPayload {
permission: ResourcePermission
target_id: string
}
43 changes: 28 additions & 15 deletions ui/src/components/COMPONENT_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ src/components/
├── mk-form-list/
│ └── index.vue # 可动态增删的表单行列表,手动导入
├── codemirror-editor/
│ ├── index.vue # 支持异步诊断和全屏编辑的代码编辑器
│ └── types.ts # 编辑器行列诊断类型
│ ├── python.vue # 内置 pylint 诊断和全屏编辑的 Python 编辑器
│ └── Json.vue # 支持格式化、语法诊断和全屏编辑的 JSON 输入框
└── mk-source-card/
│ ├── index.vue # 来源资源的统一卡片结构,手动导入
│ ├── mk-source-card-action.vue # 卡片悬浮操作容器
Expand All @@ -99,7 +99,8 @@ Vue 模板中使用,不需要手动导入。其他共享组件必须从具体
```ts
import MkSearchList from '@/components/mk-search-list/index.vue'
import MkFormList from '@/components/mk-form-list/index.vue'
import CodemirrorEditor from '@/components/codemirror-editor/index.vue'
import PythonCodeEditor from '@/components/codemirror-editor/python.vue'
import JsonInput from '@/components/codemirror-editor/Json.vue'
import MkSourceCard from '@/components/mk-source-card/index.vue'
import FolderTree from '@/components/business/folder-tree/index.vue'
import ModelSelect from '@/components/business/model-select/index.vue'
Expand All @@ -116,7 +117,8 @@ import WorkspaceRelationTags from '@/components/business/workspace-relation-tags
- 跨页面复用且依赖业务类型、固定业务接口或领域交互的组件放入 `business/<component-name>`,
由使用方手动导入。业务组件不使用 `Mk` 前缀,也不再按 Workspace 等上级领域增加额外目录。
- 不依赖固定业务的共享组合 UI 组件放在 `components/<component-name>`,由使用方手动导入。
- 一个公共组件使用一个 kebab-case 目录,入口统一为 `index.vue`。
- 一个公共组件默认使用一个 kebab-case 目录,入口统一为 `index.vue`;`codemirror-editor` 按语言
提供 `python.vue` 和 `Json.vue` 两个专用入口。
- 组件名使用 PascalCase;目录名使用 kebab-case,例如 `MkIcon` 对应
`mk-icon/index.vue`。
- `index.vue` 必须通过 `defineOptions({ name: 'ComponentName' })` 声明多单词组件名,避免
Expand Down Expand Up @@ -495,25 +497,36 @@ const paginationConfig = ref({

## 手动导入组件

### CodemirrorEditor
### PythonCodeEditor

基于 CodeMirror 6 的代码编辑器,当前内置 Python 语法和 One Dark 主题。通过 `v-model` 管理代码,
通过 `lint` 传入异步检查函数;检查结果使用 `CodeLintIssue` 的行、列、结束位置、严重程度和消息
生成编辑器诊断,组件最多展示 50 条诊断,并在代码停止输入 500ms 后检查。编辑器提供内置全屏
基于 CodeMirror 6 的 Python 代码编辑器,通过 `v-model` 管理代码,并在组件内部调用工具 pylint
接口生成诊断。组件最多展示 50 条诊断,并在代码停止输入 500ms 后检查。编辑器提供内置全屏
入口;全屏确认时更新 `v-model` 并触发 `submit-dialog`,`header-extra` 插槽用于添加全屏标题栏操作。

```vue
<script setup lang="ts">
import CodemirrorEditor from '@/components/codemirror-editor/index.vue'
import PythonCodeEditor from '@/components/codemirror-editor/python.vue'

const code = defineModel<string>({ required: true })
</script>

function lintCode(sourceCode: string) {
return ToolApi.postToolPylint(sourceCode)
}
<PythonCodeEditor v-model="code" title="工具内容(Python)" />
```

### JsonInput

JSON 专用输入框,通过 `v-model` 接收并回传解析后的 JSON 值,内置 JSON 语法诊断、格式化和全屏
编辑。组件暴露 `validateRules`,可直接接入 Element Plus 表单自定义校验器;无法解析的输入不会
覆盖最后一次有效的 `v-model` 值。

```vue
<script setup lang="ts">
import JsonInput from '@/components/codemirror-editor/Json.vue'

const config = defineModel<unknown>({ required: true })
</script>

<CodemirrorEditor v-model="code" :lint="lintCode" title="工具内容(Python)" />
<JsonInput v-model="config" title="配置(JSON)" />
```

### MkSourceCard
Expand Down Expand Up @@ -722,13 +735,13 @@ Workspace 的文件夹虚拟树业务组件。组件根据 `workspaceId` 和 `so

```vue
<script setup lang="ts">
import { FOLDER_SOURCE } from '@/api/types'
import { RESOURCE_TYPE } from '@/api/enums'
import FolderTree from '@/components/business/folder-tree/index.vue'
</script>

<FolderTree
v-model="currentFolderId"
:source="FOLDER_SOURCE.TOOL"
:source="RESOURCE_TYPE.TOOL"
:workspace-id="workspaceId"
@select="handleFolderSelect"
>
Expand Down
12 changes: 9 additions & 3 deletions ui/src/components/business/folder-tree/FolderFormDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import { reactive, ref, useTemplateRef } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import FolderApi from '@/api/admin/workspace/folder'
import type { FolderItem, FolderSource } from '@/api/types'
import { useStore } from '@/stores'
import { MsgSuccess } from '@/utils/message'

defineOptions({ name: 'FolderFormDialog' })

const { user } = useStore()

const props = defineProps<{
title: string
source: FolderSource
Expand Down Expand Up @@ -41,9 +44,12 @@ function handleSubmit() {

return request
.then((folder) => {
MsgSuccess(isEdit ? '保存成功' : '创建成功')
visible.value = false
emit('refresh', folder, isEdit)
const refreshCurrentUser = isEdit ? Promise.resolve() : user.loadCurrentUser()
return refreshCurrentUser.then(() => {
MsgSuccess(isEdit ? '保存成功' : '创建成功')
visible.value = false
emit('refresh', folder, isEdit)
})
})
.finally(() => {
loading.value = false
Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/business/folder-tree/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { computed, onMounted, ref, useTemplateRef } from 'vue'
import FolderApi from '@/api/admin/workspace/folder'
import type { FolderSource, FolderItem } from '@/api/types'
import { FOLDER_SOURCE } from '@/api/enums'
import { RESOURCE_TYPE } from '@/api/enums'
import { FOLDER_SORT, type FolderSort } from './types'
import { FOLDER_ENTRIES, FOLDER_ENTRY_ID } from '@/constants'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
Expand Down Expand Up @@ -345,7 +345,7 @@ defineExpose({ refresh: loadFolders, openCreate: handleOpenCreateFolder })

<div class="px-4 mb-1">
<MkListItem
v-if="source !== FOLDER_SOURCE.APPLICATION"
v-if="source !== RESOURCE_TYPE.APPLICATION"
:active="currentNodeKey === folderEntries.shared.id"
@click="handleFolderClick(folderEntries.shared)"
>
Expand Down
Loading
Loading