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
11 changes: 7 additions & 4 deletions ui/src/layout/AppLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ import LayoutSidebar from './LayoutSidebar.vue'

const route = useRoute()

withDefaults(defineProps<{ preview?: boolean }>(), { preview: false })

const mode = computed<LayoutMode>(() => route.meta.scope ?? 'workspace')
</script>

<template>
<div class="mk-layout h-screen overflow-hidden">
<LayoutHeader :mode="mode"> </LayoutHeader>
<div class="mk-layout overflow-hidden" :class="preview ? 'h-full' : 'h-screen'">
<LayoutHeader :mode="preview ? 'workspace' : mode" :preview="preview" />
<div class="flex">
<LayoutSidebar :mode="mode" />
<aside v-if="preview" class="h-layout-content w-sidebar"></aside>
<LayoutSidebar v-else :mode="mode" />
<main class="mk-layout__main h-layout-content w-full overflow-hidden rounded-tl-xl bg-white transition-[margin] duration-200">
<RouterView />
<RouterView v-if="!preview" />
</main>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions ui/src/layout/LayoutHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { isSystem, isWorkspace } from './utils'
import LogoFull from '@/components/mk-logo/LogoFull.vue'
import type { LayoutMode } from './types'
const router = useRouter()
const props = withDefaults(defineProps<{ mode?: LayoutMode }>(), { mode: 'workspace' })
const props = withDefaults(defineProps<{ mode?: LayoutMode; preview?: boolean }>(), { mode: 'workspace', preview: false })
function switchMode() {
router.push(isWorkspace(props.mode) ? { name: 'system-home' } : { name: 'workspace-home', params: { workspaceId: 'default' } })
}
Expand All @@ -17,7 +17,7 @@ function switchMode() {
<header class="flex-between h-header px-6">
<div class="flex items-center">
<LogoFull class="h-9 shrink-0" />
<div class="flex items-center gap-5 ml-5">
<div v-if="!preview" class="flex items-center gap-5 ml-5">
<el-divider direction="vertical" />
<!-- 企业版: 工作空间下拉框 -->
<HeaderWorkspaceDropdown v-if="isWorkspace(mode)" />
Expand All @@ -26,7 +26,7 @@ function switchMode() {
</div>

<div class="flex items-center gap-5">
<el-button class="bg-primary-gradient" round>
<el-button v-if="!preview" class="bg-primary-gradient" round>
<MkIcon name="icon_launch_outlined" />
<span>升级</span>
</el-button>
Expand Down
11 changes: 7 additions & 4 deletions ui/src/styles/element-plus.scss
Original file line number Diff line number Diff line change
Expand Up @@ -331,10 +331,7 @@
--el-dialog-title-font-size: 16px;
padding: 0;
&__header {
align-items: center;
display: flex;
height: 60px;
padding: 0 var(--el-dialog-padding-primary);
padding: var(--el-dialog-padding-primary);
}
&__headerbtn {
right: 20px;
Expand Down Expand Up @@ -370,6 +367,12 @@
}
// 专属带侧边栏结构的dialog
&.mk-aside-content-dialog {
.el-dialog__header {
align-items: center;
display: flex;
height: 60px;
padding: 0 var(--el-dialog-padding-primary);
}
.el-dialog__body {
padding: 0;
border-top: var(--el-border);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function handleRefresh() {
</template>
<div class="min-w-0">
<p class="leading-5">高级智能体</p>
<p class="whitespace-normal text-sm text-N500">使用低代码拖拉拽方式,灵活编排复杂逻辑、功能丰富的智能体</p>
<p class="text-sm text-N500">使用低代码拖拉拽方式,灵活编排复杂逻辑、功能丰富的智能体</p>
</div>
</MkDropdownItem>
<el-upload ref="elUploadRef" action="#" :auto-upload="false" class="w-full" :file-list="[]" :limit="1" :on-change="handleImportCreate" :show-file-list="false">
Expand Down
16 changes: 12 additions & 4 deletions ui/src/views/login/LoginView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,24 @@ const isLoading = ref(true)
const loginConfig = ref<LoginConfig>({ default_value: LOGIN_METHOD.LOCAL, login_methods: [LOGIN_METHOD.LOCAL], max_attempts: 1 })
const loginMode = ref<'account' | 'qr-code'>('account')

const qrCodeProviders = computed(() => (loginConfig.value.login_methods ?? []).filter((method): method is QrCodeProvider => qrCodeLoginMethods.includes(method as QrCodeProvider)))
const accountLoginMethods = computed(() => (loginConfig.value.login_methods ?? []).filter((method) => !qrCodeLoginMethods.includes(method as QrCodeProvider)))
const qrCodeProviders = computed(() =>
(loginConfig.value.login_methods ?? []).filter((method): method is QrCodeProvider => qrCodeLoginMethods.includes(method as QrCodeProvider)),
)
const accountLoginMethods = computed(() =>
(loginConfig.value.login_methods ?? []).filter((method) => !qrCodeLoginMethods.includes(method as QrCodeProvider)),
)
const accountLoginConfig = computed<LoginConfig>(() => ({
...loginConfig.value,
default_value: qrCodeLoginMethods.includes(loginConfig.value.default_value as QrCodeProvider) ? LOGIN_METHOD.LOCAL : loginConfig.value.default_value,
default_value: qrCodeLoginMethods.includes(loginConfig.value.default_value as QrCodeProvider)
? LOGIN_METHOD.LOCAL
: loginConfig.value.default_value,
login_methods: accountLoginMethods.value,
}))
const qrCodeLoginConfig = computed<LoginConfig>(() => ({
...loginConfig.value,
default_value: qrCodeLoginMethods.includes(loginConfig.value.default_value as QrCodeProvider) ? loginConfig.value.default_value : LOGIN_METHOD.WECOM,
default_value: qrCodeLoginMethods.includes(loginConfig.value.default_value as QrCodeProvider)
? loginConfig.value.default_value
: LOGIN_METHOD.WECOM,
login_methods: qrCodeProviders.value,
}))
const showLoginModeSwitch = computed(() => accountLoginMethods.value.length > 0 && qrCodeProviders.value.length > 0)
Expand Down
31 changes: 22 additions & 9 deletions ui/src/views/login/components/LoginLayout.vue
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
<script setup lang="ts">
import { computed } from 'vue'
import type { ThemeInfo } from '@/api/admin/auth/types'
import LogoFull from '@/components/mk-logo/LogoFull.vue'
import { DEFAULT_THEME_SETTING, getThemeImg } from '@/constants'
import { DEFAULT_THEME_COLOR, DEFAULT_THEME_SETTING, getThemeImg } from '@/constants'
import { useStore } from '@/stores'
import DefaultThemeAnimation from './DefaultThemeAnimation.vue'

defineOptions({ name: 'LoginLayout' })

withDefaults(defineProps<{ preview?: boolean }>(), { preview: false })
const props = withDefaults(defineProps<{ preview?: boolean; themeInfo?: ThemeInfo }>(), { preview: false })

defineSlots<{ default: () => unknown }>()

const { theme } = useStore()

const customLoginImage = computed(() => theme.themeInfo?.loginImage?.trim() ?? '')
const showDefaultThemeAnimation = computed(() => !customLoginImage.value && theme.isDefaultTheme)
const loginImage = computed(() => customLoginImage.value || getThemeImg(theme.themeInfo?.theme))
const layoutThemeInfo = computed(() => props.themeInfo ?? theme.themeInfo)
const customLoginImage = computed(() => layoutThemeInfo.value?.loginImage?.trim() ?? '')
const isDefaultTheme = computed(() => !layoutThemeInfo.value?.theme || layoutThemeInfo.value.theme === DEFAULT_THEME_COLOR)
const showDefaultThemeAnimation = computed(() => !props.preview && !customLoginImage.value && isDefaultTheme.value)
const loginImage = computed(() => customLoginImage.value || getThemeImg(layoutThemeInfo.value?.theme))
</script>

<template>
<div class="login-layout">
<div class="login-layout" :class="{ 'is-preview': preview }">
<div class="login-background"></div>

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

Expand All @@ -51,6 +55,15 @@ const loginImage = computed(() => customLoginImage.value || getThemeImg(theme.th
min-height: 100vh;
overflow: hidden;
position: relative;

&.is-preview {
height: 100%;
min-height: 0;

.login-main {
height: 100%;
}
}
}

.login-background {
Expand Down
47 changes: 40 additions & 7 deletions ui/src/views/login/modes/AccountLogin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface AccountLoginForm {

defineOptions({ name: 'AccountLogin' })

const props = defineProps<{ loginConfig?: LoginConfig }>()
const props = withDefaults(defineProps<{ loginConfig?: LoginConfig; preview?: boolean }>(), { preview: false })

const route = useRoute()
const router = useRouter()
Expand Down Expand Up @@ -50,6 +50,7 @@ const accountLoginRules = reactive<FormRules<AccountLoginForm>>({
})

const handleLogin = async () => {
if (props.preview) return
if (!accountLoginFormRef.value) return

await accountLoginFormRef.value.validate((valid) => {
Expand Down Expand Up @@ -88,13 +89,15 @@ const handleLogin = async () => {
}

const refreshCaptcha = () => {
if (props.preview) return
if (loginMethod.value === LOGIN_METHOD.LDAP) return
LoginApi.getCaptcha(accountLoginForm.username).then((res) => {
identifyCode.value = res.captcha
})
}

const selectLoginMethod = (method: LoginMethod) => {
if (props.preview) return
if (method !== LOGIN_METHOD.LOCAL && method !== LOGIN_METHOD.LDAP) {
void redirectExternalLogin(method, true)
return
Expand Down Expand Up @@ -149,6 +152,7 @@ const getExternalLoginUrl = (authType: LoginMethod): Promise<string> => {
}

onMounted(() => {
if (props.preview) return
const loginMethods = accountLoginMethods.value
if (route.query.login_mode !== 'manual') {
const loginMethod = loginMethods[0]
Expand All @@ -160,22 +164,45 @@ onMounted(() => {
</script>

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

<el-form ref="accountLoginFormRef" :model="accountLoginForm" :rules="accountLoginRules" class="login-form" @submit.prevent="handleLogin" size="large">
<el-form
ref="accountLoginFormRef"
:model="accountLoginForm"
:rules="accountLoginRules"
:autocomplete="preview ? 'off' : undefined"
class="login-form"
size="large"
@submit.prevent="handleLogin"
>
<el-form-item prop="username">
<el-input v-model="accountLoginForm.username" placeholder="请输入用户名" @blur="refreshCaptcha" />
<el-input
v-model="accountLoginForm.username"
:autocomplete="preview ? 'off' : 'username'"
:disabled="preview"
:readonly="preview"
placeholder="请输入用户名"
@blur="refreshCaptcha"
/>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="accountLoginForm.password" placeholder="请输入密码" show-password type="password" />
<el-input
v-model="accountLoginForm.password"
:autocomplete="preview ? 'new-password' : 'current-password'"
:disabled="preview"
:readonly="preview"
placeholder="请输入密码"
show-password
type="password"
/>
</el-form-item>
<div v-if="loginMethod !== LOGIN_METHOD.LDAP && identifyCode" class="flex gap-2">
<el-form-item prop="captcha" class="flex-1">
<el-input v-model="accountLoginForm.captcha" autocomplete="off" placeholder="请输入验证码" />
<el-input v-model="accountLoginForm.captcha" autocomplete="off" :disabled="preview" placeholder="请输入验证码" :readonly="preview" />
</el-form-item>
<img :src="identifyCode" alt="验证码" class="w-35 h-10 cursor-pointer border" @click="refreshCaptcha" />
</div>
Expand All @@ -199,4 +226,10 @@ onMounted(() => {
</div>
</template>

<style scoped lang="scss"></style>
<style scoped lang="scss">
.account-login.is-preview :deep(.el-input.is-disabled) {
.el-input__wrapper {
background-color: var(--el-fill-color-blank);
}
}
</style>
24 changes: 21 additions & 3 deletions ui/src/views/system/identity/users/UserListView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { datetimeFormat } from '@/utils/time'
import { LOGIN_METHOD_LABELS } from '@/constants/auth.ts'
import WorkspaceRelationTags from '@/components/business/workspace-relation-tags/index.vue'
import UserFromDrawer from './UserFromDrawer.vue'
import ImportUsersDialog from './dialog/ImportUsersDialog.vue'
import UserPwdDialog from './dialog/UserPwdDialog.vue'
import BatchSetUserRoleDialog from './dialog/BatchSetUserRoleDialog.vue'

Expand All @@ -21,6 +22,13 @@ function handleOpenUserFormDrawer(systemUser?: SystemUser) {
userFormDrawerRef.value?.open(systemUser)
}

/* 导入用户 */
const importUsersDialogRef = ref<InstanceType<typeof ImportUsersDialog>>()

function handleOpenImportUsersDialog() {
importUsersDialogRef.value?.open()
}

/* 列表查询相关 */
const userTableRef = useTemplateRef('userTableRef')
const systemUsersLoading = ref(false)
Expand Down Expand Up @@ -138,7 +146,7 @@ onMounted(() => loadSystemUsers())
<h4>{{ title }}</h4>
<div class="flex items-center">
<MkComplexSearch :fields="searchFields" @change="handleSearchChange" />
<el-button plain class="ml-3">
<el-button plain class="ml-3" @click="handleOpenImportUsersDialog">
<MkIcon name="icon_import_outlined" />
<span>导入用户</span>
</el-button>
Expand Down Expand Up @@ -180,14 +188,23 @@ onMounted(() => loadSystemUsers())

<el-table-column v-if="auth.isEE || auth.isPE" prop="role_name" width="180" label="角色">
<template #default="{ row }">
<WorkspaceRelationTags :table-render-params="{ property: '角色', value: '工作空间' }" :tags="row.role_name" :tag-workspace="row.role_workspace" />
<WorkspaceRelationTags
:table-render-params="{ property: '角色', value: '工作空间' }"
:tags="row.role_name"
:tag-workspace="row.role_workspace"
/>
</template>
</el-table-column>

<el-table-column prop="user_group_names" width="180" label="用户组">
<template #default="{ row }">
<span v-if="!row.user_group_names?.length">-</span>
<WorkspaceRelationTags v-else :table-render-params="{ property: '用户组', value: '工作空间' }" :tags="row.user_group_names" :tag-workspace="row.user_group_workspace" />
<WorkspaceRelationTags
v-else
:table-render-params="{ property: '用户组', value: '工作空间' }"
:tags="row.user_group_names"
:tag-workspace="row.user_group_workspace"
/>
</template>
</el-table-column>

Expand Down Expand Up @@ -243,6 +260,7 @@ onMounted(() => loadSystemUsers())
</template>
</MkViewLayout>
<UserFromDrawer ref="userFormDrawerRef" @refresh="loadSystemUsers" />
<ImportUsersDialog ref="importUsersDialogRef" />
<UserPwdDialog ref="userPwdDialogRef" @refresh="loadSystemUsers(false)" />
<BatchSetUserRoleDialog ref="batchSetUserRoleDialogRef" @refresh="loadSystemUsers(false)" />
</template>
Expand Down
Loading
Loading