mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: 访问限制中新增身份验证设置
This commit is contained in:
parent
aa8e68a688
commit
7a0f15b4e4
|
|
@ -312,6 +312,20 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
if 'show_source' in instance and instance.get('show_source') is not None:
|
||||
application_access_token.show_source = instance.get('show_source')
|
||||
application_access_token.save()
|
||||
application_setting_model = DBModelManage.get_model('application_setting')
|
||||
X_PACK_LICENSE_IS_VALID = (settings.XPACK_LICENSE_IS_VALID if hasattr(settings,
|
||||
'XPACK_LICENSE_IS_VALID') else False)
|
||||
if application_setting_model is not None and X_PACK_LICENSE_IS_VALID:
|
||||
application_setting, _ = application_setting_model.objects.get_or_create(
|
||||
application_id=self.data.get('application_id'))
|
||||
if application_setting is not None:
|
||||
application_setting.authentication = instance.get('authentication')
|
||||
application_setting.authentication_value = {
|
||||
"type": "password",
|
||||
"value": instance.get('authentication_value')
|
||||
}
|
||||
application_setting.save()
|
||||
|
||||
get_application_access_token(application_access_token.access_token, False)
|
||||
return self.one(with_valid=False)
|
||||
|
||||
|
|
@ -734,7 +748,8 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
'draggable': application_setting.draggable,
|
||||
'show_guide': application_setting.show_guide,
|
||||
'avatar': application_setting.avatar,
|
||||
'float_icon': application_setting.float_icon}
|
||||
'float_icon': application_setting.float_icon,
|
||||
'authentication': application_setting.authentication}
|
||||
return ApplicationSerializer.Query.reset_application(
|
||||
{**ApplicationSerializer.ApplicationModel(application).data,
|
||||
'stt_model_id': application.stt_model_id,
|
||||
|
|
|
|||
|
|
@ -387,6 +387,15 @@ const updatePlatformStatus: (application_id: string, data: any) => Promise<Resul
|
|||
) => {
|
||||
return post(`/platform/${application_id}/status`, data)
|
||||
}
|
||||
/**
|
||||
* 验证密码
|
||||
*/
|
||||
const validatePassword: (application_id: string, password: string) => Promise<Result<any>> = (
|
||||
application_id,
|
||||
password
|
||||
) => {
|
||||
return get(`/application/${application_id}/auth/${password}`, undefined)
|
||||
}
|
||||
|
||||
export default {
|
||||
getAllAppilcation,
|
||||
|
|
@ -419,5 +428,6 @@ export default {
|
|||
getPlatformStatus,
|
||||
getPlatformConfig,
|
||||
updatePlatformConfig,
|
||||
updatePlatformStatus
|
||||
updatePlatformStatus,
|
||||
validatePassword
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ export default {
|
|||
dialogTitle: 'Access Restrictions',
|
||||
showSourceLabel: 'Show Source',
|
||||
clientQueryLimitLabel: 'Each Client Query Limit',
|
||||
authentication: 'Authentication',
|
||||
authenticationValue: 'Authentication Password',
|
||||
timesDays: 'Times/Day',
|
||||
whitelistLabel: 'Whitelist',
|
||||
whitelistPlaceholder:
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ export default {
|
|||
showSourceLabel: '显示知识来源',
|
||||
clientQueryLimitLabel: '每个客户端提问限制',
|
||||
timesDays: '次/天',
|
||||
authentication: '身份验证',
|
||||
authenticationValue: '验证密码',
|
||||
whitelistLabel: '白名单',
|
||||
whitelistPlaceholder:
|
||||
'请输入允许嵌入第三方的源地址,一行一个,如:\nhttp://127.0.0.1:5678\nhttps://dataease.io',
|
||||
|
|
|
|||
|
|
@ -119,6 +119,18 @@ const useApplicationStore = defineStore({
|
|||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
async validatePassword(id: string, password: string, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
applicationApi
|
||||
.validatePassword(id, password)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -27,6 +27,30 @@
|
|||
$t('views.applicationOverview.appInfo.LimitDialog.timesDays')
|
||||
}}</span>
|
||||
</el-form-item>
|
||||
<!-- 身份验证 -->
|
||||
<el-form-item
|
||||
:label="$t('views.applicationOverview.appInfo.LimitDialog.authentication')"
|
||||
v-hasPermission="new ComplexPermission([], ['x-pack'], 'OR')"
|
||||
>
|
||||
<el-switch size="small" v-model="form.authentication"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.authentication"
|
||||
:label="$t('views.applicationOverview.appInfo.LimitDialog.authenticationValue')"
|
||||
v-hasPermission="new ComplexPermission([], ['x-pack'], 'OR')"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.authentication_value"
|
||||
readonly
|
||||
style="width: 300px; margin-right: 10px"
|
||||
></el-input>
|
||||
<el-button type="primary" text @click="copyClick(form.authentication_value)">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
</el-button>
|
||||
<el-button @click="refreshAuthentication" type="primary" text style="margin-left: 1px">
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('views.applicationOverview.appInfo.LimitDialog.whitelistLabel')"
|
||||
@click.prevent
|
||||
|
|
@ -61,6 +85,8 @@ import type { FormInstance, FormRules } from 'element-plus'
|
|||
import applicationApi from '@/api/application'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
import { copyClick } from '@/utils/clipboard'
|
||||
import { ComplexPermission } from '@/utils/permission/type'
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
|
|
@ -73,7 +99,9 @@ const limitFormRef = ref()
|
|||
const form = ref<any>({
|
||||
access_num: 0,
|
||||
white_active: true,
|
||||
white_list: ''
|
||||
white_list: '',
|
||||
authentication_value: '',
|
||||
authentication: false
|
||||
})
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
|
@ -93,6 +121,8 @@ const open = (data: any) => {
|
|||
form.value.access_num = data.access_num
|
||||
form.value.white_active = data.white_active
|
||||
form.value.white_list = data.white_list?.length ? data.white_list?.join('\n') : ''
|
||||
form.value.authentication_value = data.authentication_value
|
||||
form.value.authentication = data.authentication
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +133,9 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||
const obj = {
|
||||
white_list: form.value.white_list ? form.value.white_list.split('\n') : [],
|
||||
white_active: form.value.white_active,
|
||||
access_num: form.value.access_num
|
||||
access_num: form.value.access_num,
|
||||
authentication: form.value.authentication,
|
||||
authentication_value: form.value.authentication_value
|
||||
}
|
||||
applicationApi.putAccessToken(id as string, obj, loading).then((res) => {
|
||||
emit('refresh')
|
||||
|
|
@ -114,6 +146,17 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||
}
|
||||
})
|
||||
}
|
||||
function generateAuthenticationValue(length: number = 10) {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
const randomValues = new Uint8Array(length)
|
||||
window.crypto.getRandomValues(randomValues)
|
||||
return Array.from(randomValues)
|
||||
.map((value) => chars[value % chars.length])
|
||||
.join('')
|
||||
}
|
||||
function refreshAuthentication() {
|
||||
form.value.authentication_value = generateAuthenticationValue()
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,101 +1,130 @@
|
|||
<template>
|
||||
<div class="chat-embed layout-bg" v-loading="loading">
|
||||
<div class="chat-embed__header" :class="!isDefaultTheme ? 'custom-header' : ''">
|
||||
<div class="chat-width flex align-center">
|
||||
<div class="mr-12 ml-24 flex">
|
||||
<AppAvatar
|
||||
v-if="isAppIcon(applicationDetail?.icon)"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img :src="applicationDetail?.icon" alt="" />
|
||||
</AppAvatar>
|
||||
<AppAvatar
|
||||
v-else-if="applicationDetail?.name"
|
||||
:name="applicationDetail?.name"
|
||||
pinyinColor
|
||||
shape="square"
|
||||
:size="32"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h4>{{ applicationDetail?.name }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-embed__main">
|
||||
<AiChat
|
||||
ref="AiChatRef"
|
||||
v-model:data="applicationDetail"
|
||||
:available="applicationAvailable"
|
||||
:appId="applicationDetail?.id"
|
||||
:record="currentRecordList"
|
||||
:chatId="currentChatId"
|
||||
@refresh="refresh"
|
||||
@scroll="handleScroll"
|
||||
class="AiChat-embed"
|
||||
>
|
||||
<template #operateBefore>
|
||||
<div class="chat-width">
|
||||
<el-button type="primary" link class="new-chat-button mb-8" @click="newChat">
|
||||
<el-icon><Plus /></el-icon><span class="ml-4">新建对话</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</AiChat>
|
||||
</div>
|
||||
|
||||
<!-- 历史记录弹出层 -->
|
||||
<div
|
||||
v-if="applicationDetail.show_history || !user.isEnterprise()"
|
||||
@click.prevent.stop="show = !show"
|
||||
class="chat-popover-button cursor color-secondary"
|
||||
<el-dialog
|
||||
v-model="isPasswordDialogVisible"
|
||||
width="480px"
|
||||
height="236px"
|
||||
title="输入密码打开链接"
|
||||
custom-class="no-close-button"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:show-close="false"
|
||||
center
|
||||
:modal="true"
|
||||
>
|
||||
<AppIcon iconName="app-history-outlined"></AppIcon>
|
||||
</div>
|
||||
<el-input
|
||||
style="width: 400px; height: 40px"
|
||||
v-model="password"
|
||||
:placeholder="$t('login.ldap.passwordPlaceholder')"
|
||||
show-password
|
||||
/>
|
||||
<span class="input-error" v-if="passwordError">{{ passwordError }}</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="validatePassword"
|
||||
style="width: 400px; height: 40px; margin-top: 24px"
|
||||
>确定</el-button
|
||||
>
|
||||
</el-dialog>
|
||||
|
||||
<el-collapse-transition>
|
||||
<div v-show="show" class="chat-popover w-full" v-click-outside="clickoutside">
|
||||
<div class="border-b p-16-24">
|
||||
<span>历史记录</span>
|
||||
</div>
|
||||
|
||||
<el-scrollbar max-height="300">
|
||||
<div class="p-8">
|
||||
<common-list
|
||||
:data="chatLogeData"
|
||||
v-loading="left_loading"
|
||||
:defaultActive="currentChatId"
|
||||
@click="clickListHandle"
|
||||
@mouseenter="mouseenter"
|
||||
@mouseleave="mouseId = ''"
|
||||
<div v-if="isAuthenticated">
|
||||
<div class="chat-embed__header" :class="!isDefaultTheme ? 'custom-header' : ''">
|
||||
<div class="chat-width flex align-center">
|
||||
<div class="mr-12 ml-24 flex">
|
||||
<AppAvatar
|
||||
v-if="isAppIcon(applicationDetail?.icon)"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="flex-between">
|
||||
<auto-tooltip :content="row.abstract">
|
||||
{{ row.abstract }}
|
||||
</auto-tooltip>
|
||||
<div @click.stop v-if="mouseId === row.id && row.id !== 'new'">
|
||||
<el-button style="padding: 0" link @click.stop="deleteLog(row)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #empty>
|
||||
<div class="text-center">
|
||||
<el-text type="info">暂无历史记录</el-text>
|
||||
</div>
|
||||
</template>
|
||||
</common-list>
|
||||
<img :src="applicationDetail?.icon" alt="" />
|
||||
</AppAvatar>
|
||||
<AppAvatar
|
||||
v-else-if="applicationDetail?.name"
|
||||
:name="applicationDetail?.name"
|
||||
pinyinColor
|
||||
shape="square"
|
||||
:size="32"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="chatLogeData.length" class="gradient-divider lighter mt-8">
|
||||
<span>仅显示最近 20 条对话</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
<h4>{{ applicationDetail?.name }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
<div class="chat-popover-mask" v-show="show"></div>
|
||||
<div class="chat-embed__main">
|
||||
<AiChat
|
||||
ref="AiChatRef"
|
||||
v-model:data="applicationDetail"
|
||||
:available="applicationAvailable"
|
||||
:appId="applicationDetail?.id"
|
||||
:record="currentRecordList"
|
||||
:chatId="currentChatId"
|
||||
@refresh="refresh"
|
||||
@scroll="handleScroll"
|
||||
class="AiChat-embed"
|
||||
>
|
||||
<template #operateBefore>
|
||||
<div class="chat-width">
|
||||
<el-button type="primary" link class="new-chat-button mb-8" @click="newChat">
|
||||
<el-icon><Plus /></el-icon><span class="ml-4">新建对话</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</AiChat>
|
||||
</div>
|
||||
|
||||
<!-- 历史记录弹出层 -->
|
||||
<div
|
||||
v-if="applicationDetail.show_history || !user.isEnterprise()"
|
||||
@click.prevent.stop="show = !show"
|
||||
class="chat-popover-button cursor color-secondary"
|
||||
>
|
||||
<AppIcon iconName="app-history-outlined"></AppIcon>
|
||||
</div>
|
||||
|
||||
<el-collapse-transition>
|
||||
<div v-show="show" class="chat-popover w-full" v-click-outside="clickoutside">
|
||||
<div class="border-b p-16-24">
|
||||
<span>历史记录</span>
|
||||
</div>
|
||||
|
||||
<el-scrollbar max-height="300">
|
||||
<div class="p-8">
|
||||
<common-list
|
||||
:data="chatLogeData"
|
||||
v-loading="left_loading"
|
||||
:defaultActive="currentChatId"
|
||||
@click="clickListHandle"
|
||||
@mouseenter="mouseenter"
|
||||
@mouseleave="mouseId = ''"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="flex-between">
|
||||
<auto-tooltip :content="row.abstract">
|
||||
{{ row.abstract }}
|
||||
</auto-tooltip>
|
||||
<div @click.stop v-if="mouseId === row.id && row.id !== 'new'">
|
||||
<el-button style="padding: 0" link @click.stop="deleteLog(row)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #empty>
|
||||
<div class="text-center">
|
||||
<el-text type="info">暂无历史记录</el-text>
|
||||
</div>
|
||||
</template>
|
||||
</common-list>
|
||||
</div>
|
||||
<div v-if="chatLogeData.length" class="gradient-divider lighter mt-8">
|
||||
<span>仅显示最近 20 条对话</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
<div class="chat-popover-mask" v-show="show"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
|
@ -121,6 +150,10 @@ const applicationDetail = ref<any>({})
|
|||
const applicationAvailable = ref<boolean>(true)
|
||||
const chatLogeData = ref<any[]>([])
|
||||
const show = ref(false)
|
||||
const isPasswordDialogVisible = ref(false)
|
||||
const password = ref('')
|
||||
const passwordError = ref('')
|
||||
const isAuthenticated = ref(false)
|
||||
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
|
|
@ -171,6 +204,20 @@ function newChat() {
|
|||
currentRecordList.value = []
|
||||
currentChatId.value = 'new'
|
||||
}
|
||||
function validatePassword() {
|
||||
if (!password.value) {
|
||||
passwordError.value = '密码不能为空'
|
||||
return // 终止后续执行
|
||||
}
|
||||
application.validatePassword(applicationDetail?.value.id, password.value).then((res: any) => {
|
||||
if (res?.data.is_valid) {
|
||||
isAuthenticated.value = true
|
||||
isPasswordDialogVisible.value = false
|
||||
} else {
|
||||
passwordError.value = '密码错误'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getAccessToken(token: string) {
|
||||
application
|
||||
|
|
@ -189,6 +236,12 @@ function getAppProfile() {
|
|||
.asyncGetAppProfile(loading)
|
||||
.then((res: any) => {
|
||||
applicationDetail.value = res.data
|
||||
if (user.isEnterprise()) {
|
||||
isPasswordDialogVisible.value = applicationDetail?.value.authentication
|
||||
}
|
||||
if (!isPasswordDialogVisible.value) {
|
||||
isAuthenticated.value = true
|
||||
}
|
||||
if (res.data?.show_history || !user.isEnterprise()) {
|
||||
getChatLog(applicationDetail.value.id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,128 +1,155 @@
|
|||
<template>
|
||||
<div class="chat-pc layout-bg" :class="classObj" v-loading="loading">
|
||||
<div class="chat-pc__header" :class="!isDefaultTheme ? 'custom-header' : ''">
|
||||
<div class="flex align-center">
|
||||
<div class="mr-12 ml-24 flex">
|
||||
<AppAvatar
|
||||
v-if="isAppIcon(applicationDetail?.icon)"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img :src="applicationDetail?.icon" alt="" />
|
||||
</AppAvatar>
|
||||
<AppAvatar
|
||||
v-else-if="applicationDetail?.name"
|
||||
:name="applicationDetail?.name"
|
||||
pinyinColor
|
||||
shape="square"
|
||||
:size="32"
|
||||
/>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="isPasswordDialogVisible"
|
||||
width="480px"
|
||||
height="236px"
|
||||
title="输入密码打开链接"
|
||||
custom-class="no-close-button"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:show-close="false"
|
||||
center
|
||||
:modal="true"
|
||||
>
|
||||
<el-input
|
||||
style="width: 400px; height: 40px"
|
||||
v-model="password"
|
||||
:placeholder="$t('login.ldap.passwordPlaceholder')"
|
||||
show-password
|
||||
/>
|
||||
<span class="input-error" v-if="passwordError">{{ passwordError }}</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="validatePassword"
|
||||
style="width: 400px; height: 40px; margin-top: 24px"
|
||||
>确定</el-button
|
||||
>
|
||||
</el-dialog>
|
||||
|
||||
<h4>{{ applicationDetail?.name }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="chat-pc__left border-r">
|
||||
<div class="p-24 pb-0">
|
||||
<el-button class="add-button w-full primary" @click="newChat">
|
||||
<el-icon>
|
||||
<Plus />
|
||||
</el-icon>
|
||||
<span class="ml-4">新建对话</span>
|
||||
</el-button>
|
||||
<p class="mt-20 mb-8">历史记录</p>
|
||||
<div v-if="isAuthenticated">
|
||||
<div class="chat-pc__header" :class="!isDefaultTheme ? 'custom-header' : ''">
|
||||
<div class="flex align-center">
|
||||
<div class="mr-12 ml-24 flex">
|
||||
<AppAvatar
|
||||
v-if="isAppIcon(applicationDetail?.icon)"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img :src="applicationDetail?.icon" alt="" />
|
||||
</AppAvatar>
|
||||
<AppAvatar
|
||||
v-else-if="applicationDetail?.name"
|
||||
:name="applicationDetail?.name"
|
||||
pinyinColor
|
||||
shape="square"
|
||||
:size="32"
|
||||
/>
|
||||
</div>
|
||||
<h4>{{ applicationDetail?.name }}</h4>
|
||||
</div>
|
||||
<div class="left-height pt-0">
|
||||
<el-scrollbar>
|
||||
<div class="p-8 pt-0">
|
||||
<common-list
|
||||
:data="chatLogeData"
|
||||
class="mt-8"
|
||||
v-loading="left_loading"
|
||||
:defaultActive="currentChatId"
|
||||
@click="clickListHandle"
|
||||
@mouseenter="mouseenter"
|
||||
@mouseleave="mouseId = ''"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="flex-between">
|
||||
<auto-tooltip :content="row.abstract">
|
||||
{{ row.abstract }}
|
||||
</auto-tooltip>
|
||||
<div @click.stop v-if="mouseId === row.id && row.id !== 'new'">
|
||||
<el-button style="padding: 0" link @click.stop="deleteLog(row)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="chat-pc__left border-r">
|
||||
<div class="p-24 pb-0">
|
||||
<el-button class="add-button w-full primary" @click="newChat">
|
||||
<el-icon>
|
||||
<Plus />
|
||||
</el-icon>
|
||||
<span class="ml-4">新建对话</span>
|
||||
</el-button>
|
||||
<p class="mt-20 mb-8">历史记录</p>
|
||||
</div>
|
||||
<div class="left-height pt-0">
|
||||
<el-scrollbar>
|
||||
<div class="p-8 pt-0">
|
||||
<common-list
|
||||
:data="chatLogeData"
|
||||
class="mt-8"
|
||||
v-loading="left_loading"
|
||||
:defaultActive="currentChatId"
|
||||
@click="clickListHandle"
|
||||
@mouseenter="mouseenter"
|
||||
@mouseleave="mouseId = ''"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="flex-between">
|
||||
<auto-tooltip :content="row.abstract">
|
||||
{{ row.abstract }}
|
||||
</auto-tooltip>
|
||||
<div @click.stop v-if="mouseId === row.id && row.id !== 'new'">
|
||||
<el-button style="padding: 0" link @click.stop="deleteLog(row)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template #empty>
|
||||
<div class="text-center">
|
||||
<el-text type="info">暂无历史记录</el-text>
|
||||
</div>
|
||||
</template>
|
||||
</common-list>
|
||||
</div>
|
||||
<div v-if="chatLogeData.length" class="gradient-divider lighter mt-8">
|
||||
<span>仅显示最近 20 条对话</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<template #empty>
|
||||
<div class="text-center">
|
||||
<el-text type="info">暂无历史记录</el-text>
|
||||
</div>
|
||||
</template>
|
||||
</common-list>
|
||||
</div>
|
||||
<div v-if="chatLogeData.length" class="gradient-divider lighter mt-8">
|
||||
<span>仅显示最近 20 条对话</span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-pc__right">
|
||||
<div class="right-header border-b mb-24 p-16-24 flex-between">
|
||||
<h4 class="ellipsis-1" style="width: 70%">
|
||||
{{ currentChatName }}
|
||||
</h4>
|
||||
<div class="chat-pc__right">
|
||||
<div class="right-header border-b mb-24 p-16-24 flex-between">
|
||||
<h4 class="ellipsis-1" style="width: 70%">
|
||||
{{ currentChatName }}
|
||||
</h4>
|
||||
|
||||
<span class="flex align-center" v-if="currentRecordList.length">
|
||||
<AppIcon
|
||||
v-if="paginationConfig.total"
|
||||
iconName="app-chat-record"
|
||||
class="info mr-8"
|
||||
style="font-size: 16px"
|
||||
></AppIcon>
|
||||
<span v-if="paginationConfig.total" class="lighter">
|
||||
{{ paginationConfig.total }} 条提问
|
||||
<span class="flex align-center" v-if="currentRecordList.length">
|
||||
<AppIcon
|
||||
v-if="paginationConfig.total"
|
||||
iconName="app-chat-record"
|
||||
class="info mr-8"
|
||||
style="font-size: 16px"
|
||||
></AppIcon>
|
||||
<span v-if="paginationConfig.total" class="lighter">
|
||||
{{ paginationConfig.total }} 条提问
|
||||
</span>
|
||||
<el-dropdown class="ml-8">
|
||||
<AppIcon iconName="app-export" class="cursor" title="导出聊天记录"></AppIcon>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="exportMarkdown">导出 Markdown</el-dropdown-item>
|
||||
<el-dropdown-item @click="exportHTML">导出 HTML</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
<el-dropdown class="ml-8">
|
||||
<AppIcon iconName="app-export" class="cursor" title="导出聊天记录"></AppIcon>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="exportMarkdown">导出 Markdown</el-dropdown-item>
|
||||
<el-dropdown-item @click="exportHTML">导出 HTML</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
</div>
|
||||
<div class="right-height">
|
||||
<!-- 对话 -->
|
||||
<AiChat
|
||||
ref="AiChatRef"
|
||||
v-model:data="applicationDetail"
|
||||
:available="applicationAvailable"
|
||||
:appId="applicationDetail?.id"
|
||||
:record="currentRecordList"
|
||||
:chatId="currentChatId"
|
||||
@refresh="refresh"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
</AiChat>
|
||||
</div>
|
||||
<div class="right-height">
|
||||
<AiChat
|
||||
ref="AiChatRef"
|
||||
v-model:data="applicationDetail"
|
||||
:available="applicationAvailable"
|
||||
:appId="applicationDetail?.id"
|
||||
:record="currentRecordList"
|
||||
:chatId="currentChatId"
|
||||
@refresh="refresh"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
</AiChat>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collapse">
|
||||
<el-button @click="isCollapse = !isCollapse">
|
||||
<el-icon> <component :is="isCollapse ? 'Fold' : 'Expand'" /></el-icon>
|
||||
</el-button>
|
||||
<div class="collapse">
|
||||
<el-button @click="isCollapse = !isCollapse">
|
||||
<el-icon> <component :is="isCollapse ? 'Fold' : 'Expand'" /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, nextTick, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
|
@ -130,8 +157,11 @@ import { marked } from 'marked'
|
|||
import { saveAs } from 'file-saver'
|
||||
import { isAppIcon } from '@/utils/application'
|
||||
import useStore from '@/stores'
|
||||
|
||||
import useResize from '@/layout/hooks/useResize'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { t } from '@/locales'
|
||||
import authApi from '@/api/auth-setting'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
useResize()
|
||||
|
||||
const route = useRoute()
|
||||
|
|
@ -147,6 +177,10 @@ const isDefaultTheme = computed(() => {
|
|||
})
|
||||
|
||||
const isCollapse = ref(false)
|
||||
const isPasswordDialogVisible = ref(false)
|
||||
const password = ref('')
|
||||
const passwordError = ref('')
|
||||
const isAuthenticated = ref(false)
|
||||
|
||||
const classObj = computed(() => {
|
||||
return {
|
||||
|
|
@ -225,6 +259,12 @@ function getAppProfile() {
|
|||
.asyncGetAppProfile(loading)
|
||||
.then((res: any) => {
|
||||
applicationDetail.value = res.data
|
||||
if (user.isEnterprise()) {
|
||||
isPasswordDialogVisible.value = applicationDetail?.value.authentication
|
||||
}
|
||||
if (!isPasswordDialogVisible.value) {
|
||||
isAuthenticated.value = true
|
||||
}
|
||||
if (res.data?.show_history || !user.isEnterprise()) {
|
||||
getChatLog(applicationDetail.value.id)
|
||||
}
|
||||
|
|
@ -336,6 +376,21 @@ async function exportHTML(): Promise<void> {
|
|||
saveAs(blob, suggestedName)
|
||||
}
|
||||
|
||||
function validatePassword() {
|
||||
if (!password.value) {
|
||||
passwordError.value = '密码不能为空'
|
||||
return // 终止后续执行
|
||||
}
|
||||
application.validatePassword(applicationDetail?.value.id, password.value).then((res: any) => {
|
||||
if (res?.data.is_valid) {
|
||||
isAuthenticated.value = true
|
||||
isPasswordDialogVisible.value = false
|
||||
} else {
|
||||
passwordError.value = '密码错误'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
user.changeUserType(2)
|
||||
getAccessToken(accessToken)
|
||||
|
|
@ -455,4 +510,8 @@ onMounted(() => {
|
|||
}
|
||||
}
|
||||
}
|
||||
.input-error {
|
||||
color: red;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue