Skip to content

Commit 2c9cf7f

Browse files
feat: theme
1 parent 6ab574b commit 2c9cf7f

12 files changed

Lines changed: 418 additions & 139 deletions

File tree

‎ui/src/layout/AppLayout.vue‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@ import LayoutSidebar from './LayoutSidebar.vue'
77
88
const route = useRoute()
99
10+
withDefaults(defineProps<{ preview?: boolean }>(), { preview: false })
11+
1012
const mode = computed<LayoutMode>(() => route.meta.scope ?? 'workspace')
1113
</script>
1214

1315
<template>
14-
<div class="mk-layout h-screen overflow-hidden">
15-
<LayoutHeader :mode="mode"> </LayoutHeader>
16+
<div class="mk-layout overflow-hidden" :class="preview ? 'h-full' : 'h-screen'">
17+
<LayoutHeader :mode="preview ? 'workspace' : mode" :preview="preview" />
1618
<div class="flex">
17-
<LayoutSidebar :mode="mode" />
19+
<aside v-if="preview" class="h-layout-content w-sidebar"></aside>
20+
<LayoutSidebar v-else :mode="mode" />
1821
<main class="mk-layout__main h-layout-content w-full overflow-hidden rounded-tl-xl bg-white transition-[margin] duration-200">
19-
<RouterView />
22+
<RouterView v-if="!preview" />
2023
</main>
2124
</div>
2225
</div>

‎ui/src/layout/LayoutHeader.vue‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { isSystem, isWorkspace } from './utils'
77
import LogoFull from '@/components/mk-logo/LogoFull.vue'
88
import type { LayoutMode } from './types'
99
const router = useRouter()
10-
const props = withDefaults(defineProps<{ mode?: LayoutMode }>(), { mode: 'workspace' })
10+
const props = withDefaults(defineProps<{ mode?: LayoutMode; preview?: boolean }>(), { mode: 'workspace', preview: false })
1111
function switchMode() {
1212
router.push(isWorkspace(props.mode) ? { name: 'system-home' } : { name: 'workspace-home', params: { workspaceId: 'default' } })
1313
}
@@ -17,7 +17,7 @@ function switchMode() {
1717
<header class="flex-between h-header px-6">
1818
<div class="flex items-center">
1919
<LogoFull class="h-9 shrink-0" />
20-
<div class="flex items-center gap-5 ml-5">
20+
<div v-if="!preview" class="flex items-center gap-5 ml-5">
2121
<el-divider direction="vertical" />
2222
<!-- 企业版: 工作空间下拉框 -->
2323
<HeaderWorkspaceDropdown v-if="isWorkspace(mode)" />
@@ -26,7 +26,7 @@ function switchMode() {
2626
</div>
2727

2828
<div class="flex items-center gap-5">
29-
<el-button class="bg-primary-gradient" round>
29+
<el-button v-if="!preview" class="bg-primary-gradient" round>
3030
<MkIcon name="icon_launch_outlined" />
3131
<span>升级</span>
3232
</el-button>

‎ui/src/styles/element-plus.scss‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,10 +331,7 @@
331331
--el-dialog-title-font-size: 16px;
332332
padding: 0;
333333
&__header {
334-
align-items: center;
335-
display: flex;
336-
height: 60px;
337-
padding: 0 var(--el-dialog-padding-primary);
334+
padding: var(--el-dialog-padding-primary);
338335
}
339336
&__headerbtn {
340337
right: 20px;
@@ -370,6 +367,12 @@
370367
}
371368
// 专属带侧边栏结构的dialog
372369
&.mk-aside-content-dialog {
370+
.el-dialog__header {
371+
align-items: center;
372+
display: flex;
373+
height: 60px;
374+
padding: 0 var(--el-dialog-padding-primary);
375+
}
373376
.el-dialog__body {
374377
padding: 0;
375378
border-top: var(--el-border);

‎ui/src/views/application/components/ApplicationCreateDropdown.vue‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ function handleRefresh() {
7272
</template>
7373
<div class="min-w-0">
7474
<p class="leading-5">高级智能体</p>
75-
<p class="whitespace-normal text-sm text-N500">使用低代码拖拉拽方式,灵活编排复杂逻辑、功能丰富的智能体</p>
75+
<p class="text-sm text-N500">使用低代码拖拉拽方式,灵活编排复杂逻辑、功能丰富的智能体</p>
7676
</div>
7777
</MkDropdownItem>
7878
<el-upload ref="elUploadRef" action="#" :auto-upload="false" class="w-full" :file-list="[]" :limit="1" :on-change="handleImportCreate" :show-file-list="false">

‎ui/src/views/login/LoginView.vue‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,24 @@ const isLoading = ref(true)
1515
const loginConfig = ref<LoginConfig>({ default_value: LOGIN_METHOD.LOCAL, login_methods: [LOGIN_METHOD.LOCAL], max_attempts: 1 })
1616
const loginMode = ref<'account' | 'qr-code'>('account')
1717
18-
const qrCodeProviders = computed(() => (loginConfig.value.login_methods ?? []).filter((method): method is QrCodeProvider => qrCodeLoginMethods.includes(method as QrCodeProvider)))
19-
const accountLoginMethods = computed(() => (loginConfig.value.login_methods ?? []).filter((method) => !qrCodeLoginMethods.includes(method as QrCodeProvider)))
18+
const qrCodeProviders = computed(() =>
19+
(loginConfig.value.login_methods ?? []).filter((method): method is QrCodeProvider => qrCodeLoginMethods.includes(method as QrCodeProvider)),
20+
)
21+
const accountLoginMethods = computed(() =>
22+
(loginConfig.value.login_methods ?? []).filter((method) => !qrCodeLoginMethods.includes(method as QrCodeProvider)),
23+
)
2024
const accountLoginConfig = computed<LoginConfig>(() => ({
2125
...loginConfig.value,
22-
default_value: qrCodeLoginMethods.includes(loginConfig.value.default_value as QrCodeProvider) ? LOGIN_METHOD.LOCAL : loginConfig.value.default_value,
26+
default_value: qrCodeLoginMethods.includes(loginConfig.value.default_value as QrCodeProvider)
27+
? LOGIN_METHOD.LOCAL
28+
: loginConfig.value.default_value,
2329
login_methods: accountLoginMethods.value,
2430
}))
2531
const qrCodeLoginConfig = computed<LoginConfig>(() => ({
2632
...loginConfig.value,
27-
default_value: qrCodeLoginMethods.includes(loginConfig.value.default_value as QrCodeProvider) ? loginConfig.value.default_value : LOGIN_METHOD.WECOM,
33+
default_value: qrCodeLoginMethods.includes(loginConfig.value.default_value as QrCodeProvider)
34+
? loginConfig.value.default_value
35+
: LOGIN_METHOD.WECOM,
2836
login_methods: qrCodeProviders.value,
2937
}))
3038
const showLoginModeSwitch = computed(() => accountLoginMethods.value.length > 0 && qrCodeProviders.value.length > 0)

‎ui/src/views/login/components/LoginLayout.vue‎

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,36 @@
11
<script setup lang="ts">
22
import { computed } from 'vue'
3+
import type { ThemeInfo } from '@/api/admin/auth/types'
34
import LogoFull from '@/components/mk-logo/LogoFull.vue'
4-
import { DEFAULT_THEME_SETTING, getThemeImg } from '@/constants'
5+
import { DEFAULT_THEME_COLOR, DEFAULT_THEME_SETTING, getThemeImg } from '@/constants'
56
import { useStore } from '@/stores'
67
import DefaultThemeAnimation from './DefaultThemeAnimation.vue'
78
89
defineOptions({ name: 'LoginLayout' })
910
10-
withDefaults(defineProps<{ preview?: boolean }>(), { preview: false })
11+
const props = withDefaults(defineProps<{ preview?: boolean; themeInfo?: ThemeInfo }>(), { preview: false })
1112
1213
defineSlots<{ default: () => unknown }>()
1314
1415
const { theme } = useStore()
1516
16-
const customLoginImage = computed(() => theme.themeInfo?.loginImage?.trim() ?? '')
17-
const showDefaultThemeAnimation = computed(() => !customLoginImage.value && theme.isDefaultTheme)
18-
const loginImage = computed(() => customLoginImage.value || getThemeImg(theme.themeInfo?.theme))
17+
const layoutThemeInfo = computed(() => props.themeInfo ?? theme.themeInfo)
18+
const customLoginImage = computed(() => layoutThemeInfo.value?.loginImage?.trim() ?? '')
19+
const isDefaultTheme = computed(() => !layoutThemeInfo.value?.theme || layoutThemeInfo.value.theme === DEFAULT_THEME_COLOR)
20+
const showDefaultThemeAnimation = computed(() => !props.preview && !customLoginImage.value && isDefaultTheme.value)
21+
const loginImage = computed(() => customLoginImage.value || getThemeImg(layoutThemeInfo.value?.theme))
1922
</script>
2023

2124
<template>
22-
<div class="login-layout">
25+
<div class="login-layout" :class="{ 'is-preview': preview }">
2326
<div class="login-background"></div>
2427

2528
<header class="login-header flex-between">
2629
<div class="flex items-center gap-4">
27-
<LogoFull height="38" />
28-
<el-divider direction="vertical" v-if="theme.themeInfo?.slogan" />
29-
<span class="text-lg">{{ theme.themeInfo?.slogan || DEFAULT_THEME_SETTING.slogan }}</span>
30+
<img v-if="layoutThemeInfo?.loginLogo" :src="layoutThemeInfo.loginLogo" alt="MaxKB" class="h-9.5 max-w-50 object-contain object-left" />
31+
<LogoFull v-else height="38" />
32+
<el-divider v-if="layoutThemeInfo?.slogan" direction="vertical" />
33+
<span class="text-lg">{{ layoutThemeInfo?.slogan ?? DEFAULT_THEME_SETTING.slogan }}</span>
3034
</div>
3135
</header>
3236

@@ -51,6 +55,15 @@ const loginImage = computed(() => customLoginImage.value || getThemeImg(theme.th
5155
min-height: 100vh;
5256
overflow: hidden;
5357
position: relative;
58+
59+
&.is-preview {
60+
height: 100%;
61+
min-height: 0;
62+
63+
.login-main {
64+
height: 100%;
65+
}
66+
}
5467
}
5568
5669
.login-background {

‎ui/src/views/login/modes/AccountLogin.vue‎

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface AccountLoginForm {
1919
2020
defineOptions({ name: 'AccountLogin' })
2121
22-
const props = defineProps<{ loginConfig?: LoginConfig }>()
22+
const props = withDefaults(defineProps<{ loginConfig?: LoginConfig; preview?: boolean }>(), { preview: false })
2323
2424
const route = useRoute()
2525
const router = useRouter()
@@ -50,6 +50,7 @@ const accountLoginRules = reactive<FormRules<AccountLoginForm>>({
5050
})
5151
5252
const handleLogin = async () => {
53+
if (props.preview) return
5354
if (!accountLoginFormRef.value) return
5455
5556
await accountLoginFormRef.value.validate((valid) => {
@@ -88,13 +89,15 @@ const handleLogin = async () => {
8889
}
8990
9091
const refreshCaptcha = () => {
92+
if (props.preview) return
9193
if (loginMethod.value === LOGIN_METHOD.LDAP) return
9294
LoginApi.getCaptcha(accountLoginForm.username).then((res) => {
9395
identifyCode.value = res.captcha
9496
})
9597
}
9698
9799
const selectLoginMethod = (method: LoginMethod) => {
100+
if (props.preview) return
98101
if (method !== LOGIN_METHOD.LOCAL && method !== LOGIN_METHOD.LDAP) {
99102
void redirectExternalLogin(method, true)
100103
return
@@ -149,6 +152,7 @@ const getExternalLoginUrl = (authType: LoginMethod): Promise<string> => {
149152
}
150153
151154
onMounted(() => {
155+
if (props.preview) return
152156
const loginMethods = accountLoginMethods.value
153157
if (route.query.login_mode !== 'manual') {
154158
const loginMethod = loginMethods[0]
@@ -160,22 +164,45 @@ onMounted(() => {
160164
</script>
161165

162166
<template>
163-
<div class="flex h-full flex-col">
167+
<div class="account-login flex h-full flex-col" :class="{ 'is-preview': preview }">
164168
<div class="min-h-0 flex-1">
165169
<h2 class="mb-4">
166170
{{ loginMethod === LOGIN_METHOD.LDAP ? 'LDAP 登录' : LOGIN_METHOD_LABELS[loginMethod] }}
167171
</h2>
168172

169-
<el-form ref="accountLoginFormRef" :model="accountLoginForm" :rules="accountLoginRules" class="login-form" @submit.prevent="handleLogin" size="large">
173+
<el-form
174+
ref="accountLoginFormRef"
175+
:model="accountLoginForm"
176+
:rules="accountLoginRules"
177+
:autocomplete="preview ? 'off' : undefined"
178+
class="login-form"
179+
size="large"
180+
@submit.prevent="handleLogin"
181+
>
170182
<el-form-item prop="username">
171-
<el-input v-model="accountLoginForm.username" placeholder="请输入用户名" @blur="refreshCaptcha" />
183+
<el-input
184+
v-model="accountLoginForm.username"
185+
:autocomplete="preview ? 'off' : 'username'"
186+
:disabled="preview"
187+
:readonly="preview"
188+
placeholder="请输入用户名"
189+
@blur="refreshCaptcha"
190+
/>
172191
</el-form-item>
173192
<el-form-item prop="password">
174-
<el-input v-model="accountLoginForm.password" placeholder="请输入密码" show-password type="password" />
193+
<el-input
194+
v-model="accountLoginForm.password"
195+
:autocomplete="preview ? 'new-password' : 'current-password'"
196+
:disabled="preview"
197+
:readonly="preview"
198+
placeholder="请输入密码"
199+
show-password
200+
type="password"
201+
/>
175202
</el-form-item>
176203
<div v-if="loginMethod !== LOGIN_METHOD.LDAP && identifyCode" class="flex gap-2">
177204
<el-form-item prop="captcha" class="flex-1">
178-
<el-input v-model="accountLoginForm.captcha" autocomplete="off" placeholder="请输入验证码" />
205+
<el-input v-model="accountLoginForm.captcha" autocomplete="off" :disabled="preview" placeholder="请输入验证码" :readonly="preview" />
179206
</el-form-item>
180207
<img :src="identifyCode" alt="验证码" class="w-35 h-10 cursor-pointer border" @click="refreshCaptcha" />
181208
</div>
@@ -199,4 +226,10 @@ onMounted(() => {
199226
</div>
200227
</template>
201228

202-
<style scoped lang="scss"></style>
229+
<style scoped lang="scss">
230+
.account-login.is-preview :deep(.el-input.is-disabled) {
231+
.el-input__wrapper {
232+
background-color: var(--el-fill-color-blank);
233+
}
234+
}
235+
</style>

‎ui/src/views/system/identity/users/UserListView.vue‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { datetimeFormat } from '@/utils/time'
99
import { LOGIN_METHOD_LABELS } from '@/constants/auth.ts'
1010
import WorkspaceRelationTags from '@/components/business/workspace-relation-tags/index.vue'
1111
import UserFromDrawer from './UserFromDrawer.vue'
12+
import ImportUsersDialog from './dialog/ImportUsersDialog.vue'
1213
import UserPwdDialog from './dialog/UserPwdDialog.vue'
1314
import BatchSetUserRoleDialog from './dialog/BatchSetUserRoleDialog.vue'
1415
@@ -21,6 +22,13 @@ function handleOpenUserFormDrawer(systemUser?: SystemUser) {
2122
userFormDrawerRef.value?.open(systemUser)
2223
}
2324
25+
/* 导入用户 */
26+
const importUsersDialogRef = ref<InstanceType<typeof ImportUsersDialog>>()
27+
28+
function handleOpenImportUsersDialog() {
29+
importUsersDialogRef.value?.open()
30+
}
31+
2432
/* 列表查询相关 */
2533
const userTableRef = useTemplateRef('userTableRef')
2634
const systemUsersLoading = ref(false)
@@ -138,7 +146,7 @@ onMounted(() => loadSystemUsers())
138146
<h4>{{ title }}</h4>
139147
<div class="flex items-center">
140148
<MkComplexSearch :fields="searchFields" @change="handleSearchChange" />
141-
<el-button plain class="ml-3">
149+
<el-button plain class="ml-3" @click="handleOpenImportUsersDialog">
142150
<MkIcon name="icon_import_outlined" />
143151
<span>导入用户</span>
144152
</el-button>
@@ -180,14 +188,23 @@ onMounted(() => loadSystemUsers())
180188

181189
<el-table-column v-if="auth.isEE || auth.isPE" prop="role_name" width="180" label="角色">
182190
<template #default="{ row }">
183-
<WorkspaceRelationTags :table-render-params="{ property: '角色', value: '工作空间' }" :tags="row.role_name" :tag-workspace="row.role_workspace" />
191+
<WorkspaceRelationTags
192+
:table-render-params="{ property: '角色', value: '工作空间' }"
193+
:tags="row.role_name"
194+
:tag-workspace="row.role_workspace"
195+
/>
184196
</template>
185197
</el-table-column>
186198

187199
<el-table-column prop="user_group_names" width="180" label="用户组">
188200
<template #default="{ row }">
189201
<span v-if="!row.user_group_names?.length">-</span>
190-
<WorkspaceRelationTags v-else :table-render-params="{ property: '用户组', value: '工作空间' }" :tags="row.user_group_names" :tag-workspace="row.user_group_workspace" />
202+
<WorkspaceRelationTags
203+
v-else
204+
:table-render-params="{ property: '用户组', value: '工作空间' }"
205+
:tags="row.user_group_names"
206+
:tag-workspace="row.user_group_workspace"
207+
/>
191208
</template>
192209
</el-table-column>
193210

@@ -243,6 +260,7 @@ onMounted(() => loadSystemUsers())
243260
</template>
244261
</MkViewLayout>
245262
<UserFromDrawer ref="userFormDrawerRef" @refresh="loadSystemUsers" />
263+
<ImportUsersDialog ref="importUsersDialogRef" />
246264
<UserPwdDialog ref="userPwdDialogRef" @refresh="loadSystemUsers(false)" />
247265
<BatchSetUserRoleDialog ref="batchSetUserRoleDialogRef" @refresh="loadSystemUsers(false)" />
248266
</template>

0 commit comments

Comments
 (0)