feat: user login

This commit is contained in:
wangdan-fit2cloud 2025-07-02 18:37:15 +08:00
parent 75f75f0111
commit 9912b85df0
9 changed files with 49 additions and 351 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,6 +1,6 @@
<template>
<div class="chat-knowledge-source">
<div class="flex align-center mt-16" v-if="!isWorkFlow(props.type)">
<div class="flex align-center mt-16">
<span class="mr-4 color-secondary">{{ $t('chat.KnowledgeSource.title') }}</span>
<el-divider direction="vertical" />
<el-button type="primary" class="mr-8" link @click="openParagraph(data)">
@ -9,7 +9,7 @@
{{ data.paragraph_list?.length || 0 }}</el-button
>
</div>
<div class="mt-8" v-if="!isWorkFlow(props.type)">
<div class="mt-8">
<el-row :gutter="8" v-if="uniqueParagraphList?.length">
<template v-for="(item, index) in uniqueParagraphList" :key="index">
<el-col :span="12" class="mb-8">
@ -51,11 +51,10 @@
>
</div>
<el-button
v-if="isWorkFlow(props.type)"
type="primary"
link
@click="openExecutionDetail(data.execution_details)"
style="padding: 0;"
style="padding: 0"
>
<el-icon class="mr-4"><Document /></el-icon>
{{ $t('chat.executionDetails.title') }}</el-button
@ -76,16 +75,16 @@ import { getImgUrl, getNormalizedUrl } from '@/utils/utils'
const props = defineProps({
data: {
type: Object,
default: () => {}
default: () => {},
},
type: {
type: String,
default: ''
default: '',
},
executionIsRightPanel: {
type: Boolean,
required: false,
}
},
})
const emit = defineEmits(['openExecutionDetail', 'openParagraph'])
@ -100,7 +99,7 @@ function openParagraph(row: any, id?: string) {
ParagraphSourceDialogRef.value.open(row, id)
}
function openExecutionDetail(row: any) {
if(props.executionIsRightPanel){
if (props.executionIsRightPanel) {
emit('openExecutionDetail')
return
}

View File

@ -2,7 +2,9 @@
<div class="login-form-container">
<div class="login-title">
<div class="logo text-center">
<LogoFull height="45px" />
<slot name="logo">
<LogoFull height="45px" />
</slot>
</div>
<div class="sub-title text-center" v-if="subTitle">
<el-text type="info">{{ subTitle }}</el-text>
@ -16,7 +18,7 @@
<script setup lang="ts">
defineProps({
title: String,
subTitle: String
subTitle: String,
})
</script>
<style lang="scss" scoped>
@ -30,7 +32,6 @@ defineProps({
}
}
.login-card {
border-radius: 8px;
padding: 18px;
}
}

View File

@ -7,54 +7,10 @@
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { getThemeImg } from '@/utils/theme'
import useStore from '@/stores'
import { useLocalStorage } from '@vueuse/core'
import { langList, localeConfigKey, getBrowserLang } from '@/locales/index'
defineProps({
lang: {
type: Boolean,
default: true,
},
})
const { user, theme } = useStore()
const changeLang = (lang: string) => {
useLocalStorage(localeConfigKey, getBrowserLang()).value = lang
window.location.reload()
}
const currentLanguage = computed(() => {
return langList.value?.filter((v: any) => v.value === user.getLanguage())?.[0]?.label
})
const fileURL = computed(() => {
if (theme.themeInfo?.loginImage) {
if (typeof theme.themeInfo?.loginImage === 'string') {
return theme.themeInfo?.loginImage
} else {
return URL.createObjectURL(theme.themeInfo?.loginImage)
}
} else {
return ''
}
})
const loginImage = computed(() => {
if (theme.themeInfo?.loginImage) {
return `${fileURL.value}`
} else {
const imgName = getThemeImg(theme.themeInfo?.theme)
const imgPath = `${window.MaxKB.prefix}/theme/${imgName}.jpg`
const imageUrl = new URL(imgPath, import.meta.url).href
return imageUrl
}
})
</script>
<script setup lang="ts"></script>
<style lang="scss" scoped>
.login-warp {
height: 100vh;
background: url('@/assets/chat/user-login-bg.jpg') no-repeat center;
}
</style>

View File

@ -230,7 +230,7 @@
<AppIcon
v-if="paginationConfig.total"
iconName="app-chat-record"
class="info mr-8"
class="color-secondary mr-8"
style="font-size: 16px"
></AppIcon>
<span v-if="paginationConfig.total" class="lighter">

View File

@ -1,137 +0,0 @@
<template>
<UserLoginLayout>
<LoginContainer :subTitle="$t('theme.defaultSlogan')">
<h2 class="mb-24">{{ $t('views.login.resetPassword') }}</h2>
<el-form
class="reset-password-form"
ref="resetPasswordFormRef"
:model="resetPasswordForm"
:rules="rules"
>
<div class="mb-24">
<el-form-item prop="password">
<el-input
type="password"
size="large"
class="input-item"
v-model="resetPasswordForm.password"
:placeholder="$t('views.login.loginForm.password.placeholder')"
show-password
>
</el-input>
</el-form-item>
</div>
<div class="mb-24">
<el-form-item prop="re_password">
<el-input
type="password"
size="large"
class="input-item"
v-model="resetPasswordForm.re_password"
:placeholder="$t('views.login.loginForm.re_password.placeholder')"
show-password
>
</el-input>
</el-form-item>
</div>
</el-form>
<el-button size="large" type="primary" class="w-full" @click="resetPassword">{{
$t('common.confirm')
}}</el-button>
<div class="operate-container mt-12">
<el-button
size="large"
class="register"
@click="router.push('/login')"
link
type="primary"
icon="ArrowLeft"
>
{{ $t('views.login.buttons.backLogin') }}
</el-button>
</div>
</LoginContainer>
</UserLoginLayout>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { ResetPasswordRequest } from '@/api/type/user'
import LoginContainer from '@/layout/login-layout/LoginContainer.vue'
import UserLoginLayout from '@/layout/login-layout/UserLoginLayout.vue'
import { useRouter, useRoute } from 'vue-router'
import { MsgSuccess } from '@/utils/message'
import type { FormInstance, FormRules } from 'element-plus'
import UserApi from '@/api/user/user'
import { t } from '@/locales'
const router = useRouter()
const route = useRoute()
const {
params: { code, email },
} = route
const resetPasswordForm = ref<ResetPasswordRequest>({
password: '',
re_password: '',
email: '',
code: '',
})
onMounted(() => {
if (code && email) {
resetPasswordForm.value.code = code as string
resetPasswordForm.value.email = email as string
} else {
router.push('forgot_password')
}
})
const rules = ref<FormRules<ResetPasswordRequest>>({
password: [
{
required: true,
message: t('views.login.loginForm.re_password.requiredMessage'),
trigger: 'blur',
},
{
min: 6,
max: 20,
message: t('views.login.loginForm.password.lengthMessage'),
trigger: 'blur',
},
],
re_password: [
{
required: true,
message: t('views.login.loginForm.re_password.requiredMessage'),
trigger: 'blur',
},
{
min: 6,
max: 20,
message: t('views.login.loginForm.password.lengthMessage'),
trigger: 'blur',
},
{
validator: (rule, value, callback) => {
if (resetPasswordForm.value.password != resetPasswordForm.value.re_password) {
callback(new Error(t('views.login.loginForm.re_password.validatorMessage')))
} else {
callback()
}
},
trigger: 'blur',
},
],
})
const resetPasswordFormRef = ref<FormInstance>()
const loading = ref<boolean>(false)
const resetPassword = () => {
resetPasswordFormRef.value
?.validate()
.then(() => UserApi.postResetPassword(resetPasswordForm.value, loading))
.then(() => {
MsgSuccess(t('common.modifySuccess'))
router.push({ name: 'login' })
})
}
</script>
<style lang="scss" scoped></style>

View File

@ -1,137 +0,0 @@
<template>
<UserLoginLayout>
<LoginContainer :subTitle="$t('theme.defaultSlogan')">
<h2 class="mb-24">{{ $t('views.login.resetPassword') }}</h2>
<el-form
class="reset-password-form"
ref="resetPasswordFormRef"
:model="resetPasswordForm"
:rules="rules"
>
<div class="mb-24">
<el-form-item prop="password">
<el-input
type="password"
size="large"
class="input-item"
v-model="resetPasswordForm.password"
:placeholder="$t('views.login.loginForm.password.placeholder')"
show-password
>
</el-input>
</el-form-item>
</div>
<div class="mb-24">
<el-form-item prop="re_password">
<el-input
type="password"
size="large"
class="input-item"
v-model="resetPasswordForm.re_password"
:placeholder="$t('views.login.loginForm.re_password.placeholder')"
show-password
>
</el-input>
</el-form-item>
</div>
</el-form>
<el-button size="large" type="primary" class="w-full" @click="resetPassword">{{
$t('common.confirm')
}}</el-button>
<div class="operate-container mt-12">
<el-button
size="large"
class="register"
@click="router.push('/login')"
link
type="primary"
icon="ArrowLeft"
>
{{ $t('views.login.buttons.backLogin') }}
</el-button>
</div>
</LoginContainer>
</UserLoginLayout>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import LoginContainer from '@/layout/login-layout/LoginContainer.vue'
import UserLoginLayout from '@/layout/login-layout/UserLoginLayout.vue'
import type { ResetPasswordRequest } from '@/api/type/user'
import { useRouter, useRoute } from 'vue-router'
import { MsgSuccess } from '@/utils/message'
import type { FormInstance, FormRules } from 'element-plus'
import UserApi from '@/api/user/user'
import { t } from '@/locales'
const router = useRouter()
const route = useRoute()
const {
params: { code, email },
} = route
const resetPasswordForm = ref<ResetPasswordRequest>({
password: '',
re_password: '',
email: '',
code: '',
})
onMounted(() => {
if (code && email) {
resetPasswordForm.value.code = code as string
resetPasswordForm.value.email = email as string
} else {
router.push('forgot_password')
}
})
const rules = ref<FormRules<ResetPasswordRequest>>({
password: [
{
required: true,
message: t('views.login.loginForm.re_password.requiredMessage'),
trigger: 'blur',
},
{
min: 6,
max: 20,
message: t('views.login.loginForm.password.lengthMessage'),
trigger: 'blur',
},
],
re_password: [
{
required: true,
message: t('views.login.loginForm.re_password.requiredMessage'),
trigger: 'blur',
},
{
min: 6,
max: 20,
message: t('views.login.loginForm.password.lengthMessage'),
trigger: 'blur',
},
{
validator: (rule, value, callback) => {
if (resetPasswordForm.value.password != resetPasswordForm.value.re_password) {
callback(new Error(t('views.login.loginForm.re_password.validatorMessage')))
} else {
callback()
}
},
trigger: 'blur',
},
],
})
const resetPasswordFormRef = ref<FormInstance>()
const loading = ref<boolean>(false)
const resetPassword = () => {
resetPasswordFormRef.value
?.validate()
.then(() => UserApi.postResetPassword(resetPasswordForm.value, loading))
.then(() => {
MsgSuccess(t('common.modifySuccess'))
router.push({ name: 'login' })
})
}
</script>
<style lang="scss" scoped></style>

View File

@ -1,13 +1,40 @@
<template>
<UserLoginLayout v-if="!loading" v-loading="loading">
<LoginContainer
v-if="chatUser.chat_profile?.authentication_type == 'password'"
:subTitle="theme.themeInfo?.slogan || $t('theme.defaultSlogan')"
>
<LoginContainer v-if="chatUser.chat_profile?.authentication_type == 'password'">
<template #logo>
<div class="flex-center">
<el-avatar
v-if="isAppIcon(chatUser.chat_profile?.icon)"
shape="square"
:size="32"
class="mr-8"
style="background: none"
>
<img :src="chatUser.chat_profile?.icon" alt="" />
</el-avatar>
<LogoIcon v-else height="32px" class="mr-8" />
<h4>{{ chatUser.chat_profile?.application_name }}</h4>
</div>
</template>
<PasswordAuth></PasswordAuth>
</LoginContainer>
<LoginContainer v-else :subTitle="theme.themeInfo?.slogan || $t('theme.defaultSlogan')">
<LoginContainer v-else>
<template #logo>
<div class="flex-center">
<el-avatar
v-if="isAppIcon(chatUser.chat_profile?.icon)"
shape="square"
:size="32"
class="mr-8"
style="background: none"
>
<img :src="chatUser.chat_profile?.icon" alt="" />
</el-avatar>
<LogoIcon v-else height="32px" class="mr-8" />
<h4>{{ chatUser.chat_profile?.application_name }}</h4>
</div>
</template>
<h2 class="mb-24" v-if="!showQrCodeTab">
{{ loginMode == 'LOCAL' ? $t('views.login.title') : loginMode }}
</h2>
@ -75,17 +102,6 @@
>
{{ $t('views.login.buttons.login') }}
</el-button>
<div class="operate-container flex-between mt-12">
<el-button
:loading="loading"
class="forgot-password"
@click="router.push('/forgot_password')"
link
type="primary"
>
{{ $t('views.login.forgotPassword') }}?
</el-button>
</div>
</div>
<div v-if="showQrCodeTab">
<QrCodeTab :tabs="orgOptions" />
@ -147,7 +163,7 @@ import { useI18n } from 'vue-i18n'
import QrCodeTab from '@/views/login/scanCompinents/QrCodeTab.vue'
import { MsgConfirm, MsgError } from '@/utils/message.ts'
import PasswordAuth from '@/views/chat/auth/component/password.vue'
import useUserStore from '@/stores/modules/user.ts'
import { isAppIcon } from '@/utils/common'
const router = useRouter()
const { login, user, theme, chatUser } = useStore()

View File

@ -36,8 +36,8 @@ export default defineConfig((conf: any) => {
const ENV = loadEnv(mode, envDir)
const proxyConf: Record<string, string | ProxyOptions> = {}
proxyConf['/admin/api'] = {
target: 'http://47.92.195.88:8080/',
// target: 'http://127.0.0.1:8080',
// target: 'http://47.92.195.88:8080/',
target: 'http://127.0.0.1:8080',
changeOrigin: true,
}
proxyConf['/oss'] = {