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
4 changes: 4 additions & 0 deletions ui/src/api/API_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ src/api/

## 业务接口组织

`admin/system/portal.ts` 通过 Admin `/portal` 读取和局部保存门户配置,公共类型维护在
`types/portal.ts`。JSON 用于访问开关和认证配置,门户名称与 Logo 使用 FormData;保存返回完整配置。
认证配置保留未编辑字段,跨域 `cors_config` 暂按不透明对象保留,待后端定义其字段与执行协议。

- 业务 API 先按 Admin 入口下的 `auth`、`workspace`、`system` 等一级业务域归类。Workspace 和
System 内部可继续按明确的功能域建立子目录,例如 `workspace/application/`、
`workspace/tool/`、`system/chat-user/` 和 `system/settings/`;不需要分组的单资源接口直接放在
Expand Down
10 changes: 10 additions & 0 deletions ui/src/api/admin/system/chat-management/portal-setting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { get, put } from '../../core/request'
import type { PortalSetting, PortalSettingPayload } from '@/api/types'

/** 获取门户基本信息与访问配置。 */
const getPortalSetting = () => get<PortalSetting>('/portal')

/** 保存门户基本信息或访问配置。 */
const putPortalSetting = (payload: PortalSettingPayload | FormData) => put<PortalSettingPayload | FormData, PortalSetting>('/portal', payload)

export default { getPortalSetting, putPortalSetting }
1 change: 1 addition & 0 deletions ui/src/api/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export type * from './workflow-template'
export type * from './homepage'
export type * from './file'
export type * from './state'
export type * from './portal'
25 changes: 25 additions & 0 deletions ui/src/api/types/portal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Dict } from './common'

export interface PortalAuthConfig extends Dict<unknown> {
login_value?: string[]
max_attempts?: number
failed_attempts?: number
lock_time?: number
}

export interface PortalSetting {
id: string
name: string
description: string | null
logo: string | null
tab_logo: string | null
enable_public_access: boolean
enable_api: boolean
enable_knowledge_base_api: boolean
enable_auth: boolean
auth_config: PortalAuthConfig
enable_cors: boolean
cors_config: Dict<unknown>
}

export type PortalSettingPayload = Partial<Omit<PortalSetting, 'id'>>
2 changes: 1 addition & 1 deletion ui/src/assets/iconfont.js

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions ui/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ declare module 'vue' {
export interface GlobalComponents {
ApplicationIcon: typeof import('./components/global/mk-icon/ApplicationIcon.vue')['default']
KnowledgeIcon: typeof import('./components/global/mk-icon/KnowledgeIcon.vue')['default']
LayoutAside: typeof import('./components/global/mk-view-layout/layout-aside.vue')['default']
LayoutBatchFooter: typeof import('./components/global/mk-view-layout/layout-batch-footer.vue')['default']
LayoutAside: typeof import('./components/global/mk-view-layout/LayoutAside.vue')['default']
LayoutBatchFooter: typeof import('./components/global/mk-view-layout/LayoutBatchFooter.vue')['default']
LoadingIcon: typeof import('./components/global/mk-icon/LoadingIcon.vue')['default']
MdEditor: typeof import('./components/global/markdown-editor/MdEditor.vue')['default']
MdEditorMagnify: typeof import('./components/global/markdown-editor/MdEditorMagnify.vue')['default']
Expand All @@ -25,8 +25,8 @@ declare module 'vue' {
MkDialog: typeof import('./components/global/mk-dialog/index.vue')['default']
MkDrawer: typeof import('./components/global/mk-drawer/index.vue')['default']
MkDropdown: typeof import('./components/global/mk-dropdown/index.vue')['default']
MkDropdownItem: typeof import('./components/global/mk-dropdown/mk-dropdown-item.vue')['default']
MkDropdownMenu: typeof import('./components/global/mk-dropdown/mk-dropdown-menu.vue')['default']
MkDropdownItem: typeof import('./components/global/mk-dropdown/MkDropdownItem.vue')['default']
MkDropdownMenu: typeof import('./components/global/mk-dropdown/MkDropdownMenu.vue')['default']
MkEmpty: typeof import('./components/global/mk-empty/index.vue')['default']
MkFormList: typeof import('./components/global/mk-form-list/index.vue')['default']
MkIcon: typeof import('./components/global/mk-icon/index.vue')['default']
Expand All @@ -35,12 +35,12 @@ declare module 'vue' {
MkSearchInput: typeof import('./components/global/mk-search-input/index.vue')['default']
MkSlider: typeof import('./components/global/mk-slider/index.vue')['default']
MkSourceCard: typeof import('./components/global/mk-source-card/index.vue')['default']
MkSourceCardAction: typeof import('./components/global/mk-source-card/mk-source-card-action.vue')['default']
MkSourceCardActionDropdown: typeof import('./components/global/mk-source-card/mk-source-card-action-dropdown.vue')['default']
MkSourceCardAction: typeof import('./components/global/mk-source-card/MkSourceCardAction.vue')['default']
MkSourceCardActionDropdown: typeof import('./components/global/mk-source-card/MkSourceCardActionDropdown.vue')['default']
MkStatusLabel: typeof import('./components/global/mk-status-label/index.vue')['default']
MkTable: typeof import('./components/global/mk-table/index.vue')['default']
MkTableFilter: typeof import('./components/global/mk-table/mk-table-filter.vue')['default']
MkTableMoreDropdown: typeof import('./components/global/mk-table/mk-table-more-dropdown.vue')['default']
MkTableFilter: typeof import('./components/global/mk-table/MkTableFilter.vue')['default']
MkTableMoreDropdown: typeof import('./components/global/mk-table/MkTableMoreDropdown.vue')['default']
MkTagGroup: typeof import('./components/global/mk-tag-group/index.vue')['default']
MkTooltip: typeof import('./components/global/mk-tooltip/index.vue')['default']
MkViewLayout: typeof import('./components/global/mk-view-layout/index.vue')['default']
Expand All @@ -55,8 +55,8 @@ declare module 'vue' {
declare global {
const ApplicationIcon: typeof import('./components/global/mk-icon/ApplicationIcon.vue')['default']
const KnowledgeIcon: typeof import('./components/global/mk-icon/KnowledgeIcon.vue')['default']
const LayoutAside: typeof import('./components/global/mk-view-layout/layout-aside.vue')['default']
const LayoutBatchFooter: typeof import('./components/global/mk-view-layout/layout-batch-footer.vue')['default']
const LayoutAside: typeof import('./components/global/mk-view-layout/LayoutAside.vue')['default']
const LayoutBatchFooter: typeof import('./components/global/mk-view-layout/LayoutBatchFooter.vue')['default']
const LoadingIcon: typeof import('./components/global/mk-icon/LoadingIcon.vue')['default']
const MdEditor: typeof import('./components/global/markdown-editor/MdEditor.vue')['default']
const MdEditorMagnify: typeof import('./components/global/markdown-editor/MdEditorMagnify.vue')['default']
Expand All @@ -66,8 +66,8 @@ declare global {
const MkDialog: typeof import('./components/global/mk-dialog/index.vue')['default']
const MkDrawer: typeof import('./components/global/mk-drawer/index.vue')['default']
const MkDropdown: typeof import('./components/global/mk-dropdown/index.vue')['default']
const MkDropdownItem: typeof import('./components/global/mk-dropdown/mk-dropdown-item.vue')['default']
const MkDropdownMenu: typeof import('./components/global/mk-dropdown/mk-dropdown-menu.vue')['default']
const MkDropdownItem: typeof import('./components/global/mk-dropdown/MkDropdownItem.vue')['default']
const MkDropdownMenu: typeof import('./components/global/mk-dropdown/MkDropdownMenu.vue')['default']
const MkEmpty: typeof import('./components/global/mk-empty/index.vue')['default']
const MkFormList: typeof import('./components/global/mk-form-list/index.vue')['default']
const MkIcon: typeof import('./components/global/mk-icon/index.vue')['default']
Expand All @@ -76,12 +76,12 @@ declare global {
const MkSearchInput: typeof import('./components/global/mk-search-input/index.vue')['default']
const MkSlider: typeof import('./components/global/mk-slider/index.vue')['default']
const MkSourceCard: typeof import('./components/global/mk-source-card/index.vue')['default']
const MkSourceCardAction: typeof import('./components/global/mk-source-card/mk-source-card-action.vue')['default']
const MkSourceCardActionDropdown: typeof import('./components/global/mk-source-card/mk-source-card-action-dropdown.vue')['default']
const MkSourceCardAction: typeof import('./components/global/mk-source-card/MkSourceCardAction.vue')['default']
const MkSourceCardActionDropdown: typeof import('./components/global/mk-source-card/MkSourceCardActionDropdown.vue')['default']
const MkStatusLabel: typeof import('./components/global/mk-status-label/index.vue')['default']
const MkTable: typeof import('./components/global/mk-table/index.vue')['default']
const MkTableFilter: typeof import('./components/global/mk-table/mk-table-filter.vue')['default']
const MkTableMoreDropdown: typeof import('./components/global/mk-table/mk-table-more-dropdown.vue')['default']
const MkTableFilter: typeof import('./components/global/mk-table/MkTableFilter.vue')['default']
const MkTableMoreDropdown: typeof import('./components/global/mk-table/MkTableMoreDropdown.vue')['default']
const MkTagGroup: typeof import('./components/global/mk-tag-group/index.vue')['default']
const MkTooltip: typeof import('./components/global/mk-tooltip/index.vue')['default']
const MkViewLayout: typeof import('./components/global/mk-view-layout/index.vue')['default']
Expand Down
4 changes: 4 additions & 0 deletions ui/src/components/COMPONENT_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ import { MkDynamicsForm, MkDynamicsFormConstructor } from '@/components/mk-dynam
此规则适用于公共、页面和画布按钮组件;文件名、导入名、模板标签及已有的
`defineOptions.name` 保持一致,打开方法仍可命名为 `handleOpenXxx`。
- 目录使用 kebab-case,默认入口为 `index.vue`,通过 `defineOptions` 声明 PascalCase 多单词组件名。
`global/` 下除默认入口 `index.vue` 外,Vue 组件文件统一使用 PascalCase(大驼峰),例如
`MkDropdownMenu.vue`、`LayoutBatchFooter.vue`,与组件名及导入名保持一致。
Markdown 编辑器、CodeMirror 和 Logo 按下文的专用入口使用。
- Props、Emits、Slots 保持类型化。类型归属遵循 [API_README.md](../api/API_README.md),
API 与组件共用类型从 `@/api/types` 导入。
Expand Down Expand Up @@ -417,6 +419,8 @@ const avatar = ref('')
默认主题下展示内置蓝紫渐变 Logo,自定义主题下使用 Theme Store 中的当前主题色。`LogoFull`
还会优先展示 Theme Store 中配置的 `loginLogo`。两个组件都只接收可选的 `height`,其余主题与
Logo 数据统一从 Theme Store 获取。
`height` 通过 CSS 高度应用到所有图片和 SVG 分支,数字及纯数字字符串按 px 处理,也支持
`24px`、`2rem` 等 CSS 长度;例如 `:height="24"` 和 `height="24"` 均显示为 24px 高。

### MkEchart、MkLineChart

Expand Down
7 changes: 5 additions & 2 deletions ui/src/components/business/workspace-relation-tags/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ defineOptions({ name: 'WorkspaceRelationTags' })
const props = defineProps<{ tableRenderParams: Record<'property' | 'value', string>; tags?: string[]; tagWorkspace?: Record<string, string[]> }>()

const tableData = computed(() => {
return Object.entries(props.tagWorkspace ?? {}).map(([tagName, workspaces]) => ({ property: tagName, value: workspaces[0] === 'None' ? '-' : workspaces.join(', ') }))
return Object.entries(props.tagWorkspace ?? {}).map(([tagName, workspaces]) => ({
property: tagName,
value: workspaces[0] === 'None' ? '-' : workspaces.join(', '),
}))
})
</script>

<template>
<el-popover placement="bottom" trigger="hover" :width="420" :persistent="false">
<el-popover placement="bottom" trigger="hover" :width="420" :persistent="false" :show-after="300">
<template #reference>
<MkTagGroup :tags="tags" popover-disabled class="cursor-pointer" />
</template>
Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/global/mk-source-card/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import { computed, h, type FunctionalComponent } from 'vue'
import { dateFormat } from '@/utils/time'
import { hasRenderableSlotContent } from '@/utils/vnode'
import MkSourceCardAction from './mk-source-card-action.vue'
import MkSourceCardActionDropdown from './mk-source-card-action-dropdown.vue'
import MkSourceCardAction from './MkSourceCardAction.vue'
import MkSourceCardActionDropdown from './MkSourceCardActionDropdown.vue'

/**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ function handleConfirm() {

<el-scrollbar max-height="280px">
<div class="flex-column gap-2 p-3 pb-0">
<el-checkbox :indeterminate="selectionIndeterminate" :model-value="allSelected" class="mr-0!" @change="handleSelectAllChange"> 全部 </el-checkbox>
<el-checkbox :indeterminate="selectionIndeterminate" :model-value="allSelected" class="mr-0!" @change="handleSelectAllChange">
全部
</el-checkbox>
<el-checkbox-group v-model="pendingValues" class="flex-column gap-2">
<el-checkbox v-for="option in options" :key="option.value" :value="option.value" class="mk-table-filter__option mr-0! w-full min-w-0">
<span class="block min-w-0 truncate" :title="option.label">{{ option.label }}</span>
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/global/mk-table/index.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts" generic="T">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import type { TableInstance } from 'element-plus'
import LayoutBatchFooter from '../mk-view-layout/layout-batch-footer.vue'
import LayoutBatchFooter from '../mk-view-layout/LayoutBatchFooter.vue'
import { useSortable, type SortableChange } from '@/utils/use-sortable'
import { get } from 'lodash'

Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/global/mk-tag-group/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ withDefaults(
popper-class="mk-tag-group__popper"
trigger="hover"
:popper-style="{ maxWidth: '250px', width: 'auto' }"
:show-after="200"
:show-after="300"
v-if="tags?.length > 1"
>
<template #reference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ function toggleAside(event: MouseEvent) {

<template>
<div class="group relative flex h-full shrink-0 transition-[width] duration-200 ease-out" :class="asideCollapsed ? '' : 'w-sidebar-expanded'">
<aside class="h-full overflow-hidden border-r transition-[width] duration-200 ease-out" :class="asideCollapsed ? 'w-0 border-none' : 'w-sidebar-expanded'">
<aside
class="h-full overflow-hidden border-r transition-[width] duration-200 ease-out"
:class="asideCollapsed ? 'w-0 border-none' : 'w-sidebar-expanded'"
>
<div v-show="!asideCollapsed" class="flex-column h-full w-sidebar-expanded">
<LayoutAsideContent />
</div>
Expand All @@ -59,7 +62,7 @@ function toggleAside(event: MouseEvent) {
"
@click="toggleAside"
>
<MkIcon name="icon_left_outlined" :class="asideCollapsed && 'rotate-180'" :size="12" />
<MkIcon name="icon_left_outlined" :class="asideCollapsed && 'rotate-180'" :size="12" />
</button>
</MkTooltip>
</div>
Expand Down
10 changes: 7 additions & 3 deletions ui/src/components/global/mk-view-layout/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import { computed, Fragment, h, isVNode, ref, type FunctionalComponent, type VNode, type VNodeChild, useSlots } from 'vue'
import { useRoute } from 'vue-router'
import { ElScrollbar } from 'element-plus'
import LayoutAside from './layout-aside.vue'
import LayoutBatchFooter from './layout-batch-footer.vue'
import LayoutAside from './LayoutAside.vue'
import LayoutBatchFooter from './LayoutBatchFooter.vue'

defineOptions({ name: 'MkViewLayout', inheritAttrs: false })

Expand Down Expand Up @@ -97,7 +97,11 @@ const LayoutContent: FunctionalComponent = () => {
const customHeaderNodes = contentNodes.filter((node) => node.type === LayoutHeader)
const customFooterNodes = contentNodes.filter((node) => node.type === LayoutFooter)
const bodyNodes = contentNodes.filter((node) => node.type !== LayoutHeader && node.type !== LayoutFooter)
const headerNodes = customHeaderNodes.length ? customHeaderNodes : title.value ? [h('header', { class: 'flex-between shrink-0 py-4 gap-4' }, [h('h4', title.value)])] : []
const headerNodes = customHeaderNodes.length
? customHeaderNodes
: title.value
? [h('header', { class: 'flex-between shrink-0 py-4 gap-4' }, [h('h4', title.value)])]
: []
return [
...headerNodes,
h(
Expand Down
38 changes: 33 additions & 5 deletions ui/src/components/mk-logo/LogoFull.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
<script setup lang="ts">
import { useAttrs } from 'vue'
import { computed, useAttrs } from 'vue'
import { useStore } from '@/stores'

defineOptions({ name: 'LogoFull', inheritAttrs: false })

withDefaults(defineProps<{ height?: number | string }>(), { height: 30 })
const props = withDefaults(defineProps<{ height?: number | string }>(), { height: 30 })

// 显式设置 CSS 高度,避免图片的 height 属性被全局 height: auto 覆盖。
const logoHeight = computed(() => {
const height = String(props.height).trim()
return /^\d+(\.\d+)?$/.test(height) ? `${height}px` : height
})

const attrs = useAttrs()
const { theme } = useStore()
</script>

<template>
<img v-if="theme.themeInfo?.loginLogo" v-bind="attrs" alt="MaxKB" :height="height" :src="theme.themeInfo.loginLogo" class="max-w-none object-contain object-left" />
<img v-else-if="theme.isDefaultTheme" v-bind="attrs" alt="MaxKB" :height="height" src="@/assets/mk-logo/MaxKB-logo.svg" class="max-w-none" />
<svg v-else v-bind="attrs" :height="height" aria-hidden="true" class="max-w-none text-primary" viewBox="0 0 122 36" xmlns="http://www.w3.org/2000/svg">
<img
v-if="theme.themeInfo?.loginLogo"
v-bind="attrs"
alt="MaxKB"
:style="{ height: logoHeight }"
:src="theme.themeInfo.loginLogo"
class="max-w-none object-contain object-left"
/>
<img
v-else-if="theme.isDefaultTheme"
v-bind="attrs"
alt="MaxKB"
:style="{ height: logoHeight }"
src="@/assets/mk-logo/MaxKB-logo.svg"
class="max-w-none"
/>
<svg
v-else
v-bind="attrs"
:style="{ height: logoHeight }"
aria-hidden="true"
class="max-w-none text-primary"
viewBox="0 0 122 36"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M75.3094 19.0805V27.05H71.8274L71.8109 26.2436C70.5933 26.8762 69.4033 27.1925 68.2412 27.1923H67.8972C66.7033 27.1923 65.7546 26.7337 65.051 25.8166C64.5855 25.1007 64.342 24.2631 64.3513 23.4092V23.3143C64.3513 21.7489 64.9008 20.7092 65.9997 20.1953C66.4505 19.8949 67.5929 19.7447 69.4271 19.7447H71.3008V19.3058C71.3008 18.4045 71.1703 17.8867 70.9094 17.7523C70.6249 17.5388 70.1228 17.4321 69.4033 17.4321H65.6678L65.7312 14.2396L70.032 14.1233C72.5857 14.1233 74.1669 14.7558 74.7758 16.0208C75.1315 16.756 75.3094 17.7759 75.3094 19.0805ZM68.6032 22.3901C68.4844 22.5315 68.3597 22.9902 68.3597 23.3143C68.3597 24.0259 68.7234 24.3817 69.4508 24.3817C69.8855 24.3817 70.5022 24.2038 71.3008 23.848V22.1112C71.3008 22.1112 69.0361 21.875 68.6032 22.3901Z"
fill="currentColor"
Expand Down
14 changes: 10 additions & 4 deletions ui/src/components/mk-logo/LogoIcon.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
<script setup lang="ts">
import { useAttrs } from 'vue'
import { computed, useAttrs } from 'vue'
import { useStore } from '@/stores'

defineOptions({ name: 'LogoIcon', inheritAttrs: false })

withDefaults(defineProps<{ height?: number | string }>(), { height: 36 })
const props = withDefaults(defineProps<{ height?: number | string }>(), { height: 36 })

// 显式设置 CSS 高度,避免图片的 height 属性被全局 height: auto 覆盖。
const logoHeight = computed(() => {
const height = String(props.height).trim()
return /^\d+(\.\d+)?$/.test(height) ? `${height}px` : height
})

const attrs = useAttrs()
const { theme } = useStore()
</script>

<template>
<img v-if="theme.isDefaultTheme" v-bind="attrs" alt="MaxKB" :height="height" src="@/assets/mk-logo/logo.svg" class="max-w-none" />
<img v-if="theme.isDefaultTheme" v-bind="attrs" alt="MaxKB" :style="{ height: logoHeight }" src="@/assets/mk-logo/logo.svg" class="max-w-none" />
<svg
v-else
v-bind="attrs"
:height="height"
:style="{ height: logoHeight }"
class="max-w-none text-primary"
role="img"
viewBox="0 0 232.4409 232.4409"
Expand Down
2 changes: 1 addition & 1 deletion ui/src/layout/LayoutHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function switchMode() {
<template>
<header class="flex-between h-header px-6">
<div class="flex-align-center">
<LogoFull class="h-9 shrink-0" />
<LogoFull class="shrink-0" />
<div v-if="!preview" class="flex-align-center gap-5 ml-5">
<el-divider direction="vertical" />
<!-- 企业版: 工作空间下拉框 -->
Expand Down
Loading
Loading