diff --git a/ui/src/api/admin/system/theme-setting.ts b/ui/src/api/admin/system/theme-setting.ts new file mode 100644 index 00000000000..e09ade6a570 --- /dev/null +++ b/ui/src/api/admin/system/theme-setting.ts @@ -0,0 +1,11 @@ +import { get, put } from '../core/request' +import type { ThemeInfo } from '../auth/types' + +/** 保存系统外观主题设置。 */ +const putThemeSetting = (payload: FormData) => { + return put('/settings/theme', payload) +} + +export default { + putThemeSetting, +} diff --git a/ui/src/assets/appearance/platform-preview.png b/ui/src/assets/appearance/platform-preview.png deleted file mode 100644 index 46429f1b607..00000000000 Binary files a/ui/src/assets/appearance/platform-preview.png and /dev/null differ diff --git a/ui/src/assets/login-animation/default-animation-motion.gif b/ui/src/assets/login-animation/default-animation-motion.gif new file mode 100644 index 00000000000..b8735c64d86 Binary files /dev/null and b/ui/src/assets/login-animation/default-animation-motion.gif differ diff --git a/ui/src/assets/login-theme/default.png b/ui/src/assets/login-theme/default.png index fe92b22cb6b..85a350baf3b 100644 Binary files a/ui/src/assets/login-theme/default.png and b/ui/src/assets/login-theme/default.png differ diff --git a/ui/src/assets/login-theme/green.png b/ui/src/assets/login-theme/green.png new file mode 100644 index 00000000000..2c8cb1a8471 Binary files /dev/null and b/ui/src/assets/login-theme/green.png differ diff --git a/ui/src/assets/login-theme/orange.png b/ui/src/assets/login-theme/orange.png new file mode 100644 index 00000000000..139800e1020 Binary files /dev/null and b/ui/src/assets/login-theme/orange.png differ diff --git a/ui/src/assets/login-theme/purple.png b/ui/src/assets/login-theme/purple.png new file mode 100644 index 00000000000..80006a600d2 Binary files /dev/null and b/ui/src/assets/login-theme/purple.png differ diff --git a/ui/src/assets/login-theme/red.png b/ui/src/assets/login-theme/red.png new file mode 100644 index 00000000000..1cb10d15d07 Binary files /dev/null and b/ui/src/assets/login-theme/red.png differ diff --git a/ui/src/assets/logo/MaxKB-logo.svg b/ui/src/assets/mk-logo/MaxKB-logo.svg similarity index 100% rename from ui/src/assets/logo/MaxKB-logo.svg rename to ui/src/assets/mk-logo/MaxKB-logo.svg diff --git a/ui/src/assets/mk-logo/logo.svg b/ui/src/assets/mk-logo/logo.svg new file mode 100644 index 00000000000..b95b2350eca --- /dev/null +++ b/ui/src/assets/mk-logo/logo.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + MaxKB + + + + + + + \ No newline at end of file diff --git a/ui/src/assets/mk_login_background.png b/ui/src/assets/mk_login_background.png deleted file mode 100644 index 7f94ec31c3e..00000000000 Binary files a/ui/src/assets/mk_login_background.png and /dev/null differ diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index 8a6c7cf4c54..57786a6fc7b 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -89,6 +89,9 @@ src/components/ │ └── index.vue # 搜索框与剩余空间滚动列表,手动导入 ├── mk-form-list/ │ └── index.vue # 可动态增删的表单行列表,手动导入 +├── mk-logo/ +│ ├── LogoFull.vue # 带产品名称的完整 Logo,手动导入 +│ └── LogoIcon.vue # 不带产品名称的图形 Logo,手动导入 ├── codemirror-editor/ │ ├── python.vue # 内置 pylint 诊断和全屏编辑的 Python 编辑器 │ └── Json.vue # 支持格式化、语法诊断和全屏编辑的 JSON 输入框 @@ -105,6 +108,8 @@ Vue 模板中使用,不需要手动导入。其他共享组件必须从具体 import MkSearchList from '@/components/mk-search-list/index.vue' import MkFormList from '@/components/mk-form-list/index.vue' import MkDateRange from '@/components/mk-date-range/index.vue' +import LogoFull from '@/components/mk-logo/LogoFull.vue' +import LogoIcon from '@/components/mk-logo/LogoIcon.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' @@ -124,7 +129,8 @@ import WorkspaceRelationTags from '@/components/business/workspace-relation-tags 由使用方手动导入。业务组件不使用 `Mk` 前缀,也不再按 Workspace 等上级领域增加额外目录。 - 不依赖固定业务的共享组合 UI 组件放在 `components/`,由使用方手动导入。 - 一个公共组件默认使用一个 kebab-case 目录,入口统一为 `index.vue`;`codemirror-editor` 按语言 - 提供 `python.vue` 和 `Json.vue` 两个专用入口。 + 提供 `python.vue` 和 `Json.vue` 两个专用入口,`mk-logo` 按完整 Logo 与图形 Logo 提供 + `LogoFull.vue` 和 `LogoIcon.vue` 两个专用入口。 - 组件名使用 PascalCase;目录名使用 kebab-case,例如 `MkIcon` 对应 `mk-icon/index.vue`。 - `index.vue` 必须通过 `defineOptions({ name: 'ComponentName' })` 声明多单词组件名,避免 @@ -537,6 +543,27 @@ Dialog、Drawer、Popover、嵌套区域等其他大、小表格均禁止开启 ## 手动导入组件 +### LogoFull、LogoIcon + +`LogoFull` 展示带产品名称的完整 Logo,`LogoIcon` 展示不带产品名称的图形 Logo,使用时分别从 +`@/components/mk-logo/LogoFull.vue` 和 `@/components/mk-logo/LogoIcon.vue` 显式导入。两个组件在 +默认主题下展示内置蓝紫渐变 Logo,自定义主题下使用 Theme Store 中的当前主题色。`LogoFull` +还会优先展示 Theme Store 中配置的 `loginLogo`。两个组件都只接收可选的 `height`,其余主题与 +Logo 数据统一从 Theme Store 获取。 + +```vue + + + +``` + ### MkDateRange 组合日期预设下拉框和自定义日期区间选择器。默认显示“过去 7 天”,仅在用户修改筛选条件时通过 @@ -875,7 +902,8 @@ import WorkspaceRelationTags from '@/components/business/workspace-relation-tags ## 新增公共组件 1. 根据使用范围选择 `global`、`components` 直属目录、`components/business` 或所属功能目录。 -2. 在 `/index.vue` 创建组件,并声明多单词组件名。 +2. 默认在 `/index.vue` 创建组件并声明多单词组件名;已有多入口约定的目录按其 + 专用入口文件组织。 3. 使用类型化 Props、Emits 和 Slots。 4. `global` 组件在模板中直接使用;其他共享组件由使用方从具体路径导入。 5. 更新本文档中的目录树和组件说明。 diff --git a/ui/src/components/business/folder-tree/FolderFormDialog.vue b/ui/src/components/business/folder-tree/FolderFormDialog.vue index 3eab1883eca..479e763eacc 100644 --- a/ui/src/components/business/folder-tree/FolderFormDialog.vue +++ b/ui/src/components/business/folder-tree/FolderFormDialog.vue @@ -8,7 +8,7 @@ import { MsgSuccess } from '@/utils/message' defineOptions({ name: 'FolderFormDialog' }) -const { user } = useStore() +const { auth } = useStore() const props = defineProps<{ title: string @@ -44,7 +44,7 @@ function handleSubmit() { return request .then((folder) => { - const refreshCurrentUser = isEdit ? Promise.resolve() : user.loadCurrentUser() + const refreshCurrentUser = isEdit ? Promise.resolve() : auth.loadAuthBaseProfile() return refreshCurrentUser.then(() => { MsgSuccess(isEdit ? '保存成功' : '创建成功') visible.value = false diff --git a/ui/src/components/mk-logo/LogoFull.vue b/ui/src/components/mk-logo/LogoFull.vue new file mode 100644 index 00000000000..38a7b67e9d5 --- /dev/null +++ b/ui/src/components/mk-logo/LogoFull.vue @@ -0,0 +1,91 @@ + + + diff --git a/ui/src/components/mk-logo/LogoIcon.vue b/ui/src/components/mk-logo/LogoIcon.vue new file mode 100644 index 00000000000..fdefa24dd97 --- /dev/null +++ b/ui/src/components/mk-logo/LogoIcon.vue @@ -0,0 +1,64 @@ + + + diff --git a/ui/src/constants/index.ts b/ui/src/constants/index.ts index 72d5e5e6af2..ed7c32a35ed 100644 --- a/ui/src/constants/index.ts +++ b/ui/src/constants/index.ts @@ -3,3 +3,4 @@ export * from './auth' export * from './model' export * from './tool' export * from './folder' +export * from './theme' diff --git a/ui/src/constants/theme.ts b/ui/src/constants/theme.ts new file mode 100644 index 00000000000..b778f7338a0 --- /dev/null +++ b/ui/src/constants/theme.ts @@ -0,0 +1,38 @@ +import defaultThemeImage from '@/assets/login-theme/default.png' +import greenThemeImage from '@/assets/login-theme/green.png' +import orangeThemeImage from '@/assets/login-theme/orange.png' +import purpleThemeImage from '@/assets/login-theme/purple.png' +import redThemeImage from '@/assets/login-theme/red.png' + +export const DEFAULT_THEME_COLOR = '#3370ff' + +const THEME_IMAGE_MAP: Record = { + [DEFAULT_THEME_COLOR]: defaultThemeImage, + '#00b69d': greenThemeImage, + '#7f3bf5': purpleThemeImage, + '#f01d94': redThemeImage, + '#ff6000': orangeThemeImage, +} + +/** 根据主题色返回内置登录插图,自定义颜色使用默认插图。 */ +export function getThemeImg(themeColor?: string) { + return THEME_IMAGE_MAP[themeColor?.trim().toLowerCase() ?? ''] ?? defaultThemeImage +} + +// 默认设置 +export const defaultThemeSetting = { + icon: '', + loginLogo: '', + loginImage: '', + title: 'MaxKB', + slogan: '强大易用的企业级智能体平台', +} + +export const defaultPlatformSetting = { + showUserManual: true, + userManualUrl: 'https://maxkb.cn/docs/v2/', + showForum: true, + forumUrl: 'https://bbs.fit2cloud.com/c/mk/11', + showProject: true, + projectUrl: 'https://github.com/1Panel-dev/MaxKB', +} diff --git a/ui/src/layout/AppLayout.vue b/ui/src/layout/AppLayout.vue index bff9be22a85..92443018823 100644 --- a/ui/src/layout/AppLayout.vue +++ b/ui/src/layout/AppLayout.vue @@ -11,7 +11,7 @@ const mode = computed(() => route.meta.scope ?? 'workspace')
    diff --git a/ui/src/views/system/identity/roles/RoleListView.vue b/ui/src/views/system/identity/roles/RoleListView.vue index 5600f2ae2bb..6641f54fce8 100644 --- a/ui/src/views/system/identity/roles/RoleListView.vue +++ b/ui/src/views/system/identity/roles/RoleListView.vue @@ -124,7 +124,7 @@ onMounted(() => loadRoles()) > @@ -150,7 +150,7 @@ onMounted(() => loadRoles())

    {{ currentRole.role_name }}

    - + {{ currentRole.internal ? '系' : ROLE_TYPE_LABELS[currentRole.type] }} diff --git a/ui/src/views/system/settings/AppearanceSettingsView.vue b/ui/src/views/system/settings/AppearanceSettingsView.vue deleted file mode 100644 index 6a51b3579b3..00000000000 --- a/ui/src/views/system/settings/AppearanceSettingsView.vue +++ /dev/null @@ -1,312 +0,0 @@ - - - - - diff --git a/ui/src/views/system/settings/theme/LoginPreview.vue b/ui/src/views/system/settings/theme/LoginPreview.vue new file mode 100644 index 00000000000..765c1e537bb --- /dev/null +++ b/ui/src/views/system/settings/theme/LoginPreview.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/ui/src/views/system/settings/theme/ThemeSettingView.vue b/ui/src/views/system/settings/theme/ThemeSettingView.vue new file mode 100644 index 00000000000..fef41b589a8 --- /dev/null +++ b/ui/src/views/system/settings/theme/ThemeSettingView.vue @@ -0,0 +1,393 @@ + + + + + diff --git a/ui/src/views/system/settings/theme/constant.ts b/ui/src/views/system/settings/theme/constant.ts new file mode 100644 index 00000000000..e011951975b --- /dev/null +++ b/ui/src/views/system/settings/theme/constant.ts @@ -0,0 +1,29 @@ +import { DEFAULT_THEME_COLOR } from '@/constants/theme' + +export const themeList = [ + { + label: '默认', + value: DEFAULT_THEME_COLOR, + loginBackground: 'default', + }, + { + label: '活力橙', + value: '#FF6000', + loginBackground: 'orange', + }, + { + label: '松石绿', + value: '#00B69D', + loginBackground: 'green', + }, + { + label: '神秘紫', + value: '#7F3BF5', + loginBackground: 'purple', + }, + { + label: '胭脂红', + value: '#F01D94', + loginBackground: 'red', + }, +] diff --git a/ui/src/views/tool/components/ToolCard.vue b/ui/src/views/tool/components/ToolCard.vue index 2332583df6f..6eebb4f3e71 100644 --- a/ui/src/views/tool/components/ToolCard.vue +++ b/ui/src/views/tool/components/ToolCard.vue @@ -119,7 +119,7 @@ function handleExportTool(tool: ToolItem) {

    diff --git a/ui/src/views/tool/components/ToolCreateDropdown.vue b/ui/src/views/tool/components/ToolCreateDropdown.vue index 266f5e907ca..8e3ce233342 100644 --- a/ui/src/views/tool/components/ToolCreateDropdown.vue +++ b/ui/src/views/tool/components/ToolCreateDropdown.vue @@ -9,7 +9,7 @@ import ToolFormDrawer from '@/views/tool/form/ToolFormDrawer.vue' defineOptions({ name: 'ToolCreateDropdown' }) -const { user } = useStore() +const { auth } = useStore() const props = defineProps<{ folderId: string @@ -36,7 +36,7 @@ function handleImportCreate(file: UploadFile) { if (!file.raw) return ToolApi.postToolImport(file.raw, props.folderId) .then(() => { - return user.loadCurrentUser().then(() => { + return auth.loadAuthBaseProfile().then(() => { MsgSuccess('导入成功') handleRefresh() }) diff --git a/ui/src/views/tool/form/components/init-field/InitFieldTable.vue b/ui/src/views/tool/form/components/init-field/InitFieldTable.vue index 6d7d499b7bc..71fe7767abc 100644 --- a/ui/src/views/tool/form/components/init-field/InitFieldTable.vue +++ b/ui/src/views/tool/form/components/init-field/InitFieldTable.vue @@ -49,7 +49,7 @@ function handleDeleteInitField(index: number) { diff --git a/ui/src/views/tool/form/components/input-field/InputFieldTable.vue b/ui/src/views/tool/form/components/input-field/InputFieldTable.vue index c54eef97bc7..51c9417b068 100644 --- a/ui/src/views/tool/form/components/input-field/InputFieldTable.vue +++ b/ui/src/views/tool/form/components/input-field/InputFieldTable.vue @@ -45,7 +45,7 @@ function handleDeleteInputField(index: number) {