From 5f3f1dd2cac4938846e7ca0e98c1a7256f2972c6 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 <80892890+shaohuzhang1@users.noreply.github.com> Date: Mon, 22 Jul 2024 18:52:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=98=BE=E7=A4=BA=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../serializers/application_serializers.py | 19 +- apps/application/template/embed.js | 40 +--- apps/common/models/db_model_manage.py | 35 +++ apps/common/models/handle/base_handle.py | 15 ++ .../handle/impl/default_base_model_handle.py | 14 ++ apps/smartdoc/wsgi.py | 2 + ui/src/api/application-xpack.ts | 53 +++++ ui/src/components/ai-chat/index.vue | 14 +- ui/src/components/layout-container/index.vue | 2 +- .../components/top-bar/avatar/AboutDialog.vue | 25 +- .../lang/en_US/views/application-overview.ts | 205 +++++++++-------- .../lang/zh_CN/views/application-overview.ts | 204 +++++++++-------- ui/src/stores/modules/application.ts | 56 ++++- ui/src/stores/modules/user.ts | 11 +- .../component/DisplaySettingDialog.vue | 213 ++++++++++++++++++ .../component/LimitDialog.vue | 8 +- ui/src/views/application-overview/index.vue | 52 +++-- ui/src/views/authentication/index.vue | 12 +- ui/src/views/chat/base/index.vue | 7 +- ui/src/views/chat/embed/index.vue | 17 +- ui/src/views/chat/index.vue | 28 ++- ui/src/views/chat/pc/index.vue | 12 +- ui/src/views/login/index.vue | 2 +- ui/src/views/theme/index.vue | 6 +- 24 files changed, 747 insertions(+), 305 deletions(-) create mode 100644 apps/common/models/db_model_manage.py create mode 100644 apps/common/models/handle/base_handle.py create mode 100644 apps/common/models/handle/impl/default_base_model_handle.py create mode 100644 ui/src/api/application-xpack.ts create mode 100644 ui/src/views/application-overview/component/DisplaySettingDialog.vue diff --git a/apps/application/serializers/application_serializers.py b/apps/application/serializers/application_serializers.py index 7e85ffc4a..692a17843 100644 --- a/apps/application/serializers/application_serializers.py +++ b/apps/application/serializers/application_serializers.py @@ -32,6 +32,7 @@ from common.db.search import get_dynamics_model, native_search, native_page_sear from common.db.sql_execute import select_list from common.exception.app_exception import AppApiException, NotFound404, AppUnauthorizedFailed from common.field.common import UploadedImageField +from common.models.db_model_manage import DBModelManage from common.util.common import valid_license from common.util.field_message import ErrMessage from common.util.file_util import get_file_content @@ -194,10 +195,19 @@ class ApplicationSerializer(serializers.Serializer): file.close() application_access_token = QuerySet(ApplicationAccessToken).filter( access_token=self.data.get('token')).first() + is_draggable = 'false' + show_guide = 'true' + float_icon = f"{self.data.get('protocol')}://{self.data.get('host')}/ui/favicon.ico" + application_setting_model = DBModelManage.get_model('application_setting') + if application_setting_model is not None: + application_setting = QuerySet(application_setting_model).filter( + application_id=application_access_token.application_id).first() + if application_setting is not None: + is_draggable = 'true' if application_setting.is_draggable else 'false' + float_icon = f"{self.data.get('protocol')}://{self.data.get('host')}{application_setting.float_icon}" + show_guide = 'true' if application_setting else 'false' is_auth = 'true' if application_access_token is not None and application_access_token.is_active else 'false' - application_access_token = QuerySet(ApplicationAccessToken).filter( - access_token=self.data.get('token')).first() t = Template(content) s = t.render( Context( @@ -205,7 +215,10 @@ class ApplicationSerializer(serializers.Serializer): 'token': self.data.get('token'), 'white_list_str': ",".join( application_access_token.white_list), - 'white_active': 'true' if application_access_token.white_active else 'false'})) + 'white_active': 'true' if application_access_token.white_active else 'false', + 'is_draggable': is_draggable, + 'float_icon': float_icon, + 'show_guide': show_guide})) response = HttpResponse(s, status=200, headers={'Content-Type': 'text/javascript'}) return response diff --git a/apps/application/template/embed.js b/apps/application/template/embed.js index ec4e6b9f2..7006984dd 100644 --- a/apps/application/template/embed.js +++ b/apps/application/template/embed.js @@ -19,34 +19,8 @@ const guideHtml=` ` const chatButtonHtml= -`
- - - - - - - - - - - - - - - - - - - - - - - - - - - +`
+
` @@ -113,6 +87,12 @@ const initChat=(root)=>{ closeviewport.classList.remove('maxkb-viewportnone') } } + if({{is_draggable}}){ + chat_button.addEventListener("dragend",(e)=>{ + chat_button.style.top=(e.y-25)+'px' + chat_button.style.left=(e.x-25)+'px' + }) + } viewport.onclick=viewport_func closeviewport.onclick=viewport_func } @@ -127,7 +107,7 @@ function initMaxkb(){ maxkb.appendChild(root) document.body.appendChild(maxkb) const maxkbMaskTip=localStorage.getItem('maxkbMaskTip') - if(maxkbMaskTip==null){ + if(maxkbMaskTip==null && {{show_guide}}){ initGuide(root) } initChat(root) @@ -247,6 +227,8 @@ function initMaxkbStyle(root){ bottom: 30px; right: 0; cursor: pointer; + height:50px; + width:50px; } #maxkb #maxkb-chat-container{ z-index:10000;position: relative; diff --git a/apps/common/models/db_model_manage.py b/apps/common/models/db_model_manage.py new file mode 100644 index 000000000..80ce0f55b --- /dev/null +++ b/apps/common/models/db_model_manage.py @@ -0,0 +1,35 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎 + @file: db_model_manage.py + @date:2024/7/22 17:00 + @desc: +""" +from importlib import import_module +from django.conf import settings + + +def new_instance_by_class_path(class_path: str): + parts = class_path.rpartition('.') + package_path = parts[0] + class_name = parts[2] + module = import_module(package_path) + HandlerClass = getattr(module, class_name) + return HandlerClass() + + +class DBModelManage: + model_dict = {} + + @staticmethod + def get_model(model_name): + return DBModelManage.model_dict.get(model_name) + + @staticmethod + def init(): + handles = [new_instance_by_class_path(class_path) for class_path in + (settings.MODEL_HANDLES if hasattr(settings, 'MODEL_HANDLES') else [])] + for h in handles: + model_dict = h.get_model_dict() + DBModelManage.model_dict = {**DBModelManage.model_dict, **model_dict} diff --git a/apps/common/models/handle/base_handle.py b/apps/common/models/handle/base_handle.py new file mode 100644 index 000000000..17389673e --- /dev/null +++ b/apps/common/models/handle/base_handle.py @@ -0,0 +1,15 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎 + @file: base_handle.py + @date:2024/7/22 17:02 + @desc: +""" +from abc import ABC, abstractmethod + + +class IBaseModelHandle(ABC): + @abstractmethod + def get_model_dict(self): + pass diff --git a/apps/common/models/handle/impl/default_base_model_handle.py b/apps/common/models/handle/impl/default_base_model_handle.py new file mode 100644 index 000000000..b1ed7051a --- /dev/null +++ b/apps/common/models/handle/impl/default_base_model_handle.py @@ -0,0 +1,14 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎 + @file: default_base_model_handle.py + @date:2024/7/22 17:06 + @desc: +""" +from common.models.handle.base_handle import IBaseModelHandle + + +class DefaultBaseModelHandle(IBaseModelHandle): + def get_model_dict(self): + return {} diff --git a/apps/smartdoc/wsgi.py b/apps/smartdoc/wsgi.py index e04ec5274..2c11c5d9d 100644 --- a/apps/smartdoc/wsgi.py +++ b/apps/smartdoc/wsgi.py @@ -19,9 +19,11 @@ application = get_wsgi_application() def post_handler(): from common import event from common import job + from common.models.db_model_manage import DBModelManage event.run() event.ListenerManagement.init_embedding_model_signal.send() job.run() + DBModelManage.init() post_handler() diff --git a/ui/src/api/application-xpack.ts b/ui/src/api/application-xpack.ts new file mode 100644 index 000000000..f71c08ac4 --- /dev/null +++ b/ui/src/api/application-xpack.ts @@ -0,0 +1,53 @@ +import { Result } from '@/request/Result' +import { get, put } from '@/request/index' +import { type Ref } from 'vue' + +const prefix = '/application' + +/** + * 替换社区版-获取AccessToken + * @param 参数 application_id + */ +const getAccessToken: (application_id: string, loading?: Ref) => Promise> = ( + application_id, + loading +) => { + return get(`${prefix}/${application_id}/setting`, undefined, loading) +} + +/** + * 替换社区版-修改AccessToken + * @param 参数 application_id + * data { + * "show_source": boolean, + * "show_history": boolean, + * "draggable": boolean, + * "show_guide": boolean, + * "avatar": file, + * "float_icon": file, + * } + */ +const putAccessToken: ( + application_id: string, + data: any, + loading?: Ref +) => Promise> = (application_id, data, loading) => { + return put(`${prefix}/${application_id}/setting`, data, undefined, loading) +} + +/** + * 对话获取应用相关信息 + * @param 参数 + { + "access_token": "string" +} + */ +const getAppXpackProfile: (loading?: Ref) => Promise = (loading) => { + return get(`${prefix}/xpack/profile`, undefined, loading) +} + +export default { + getAccessToken, + putAccessToken, + getAppXpackProfile +} diff --git a/ui/src/components/ai-chat/index.vue b/ui/src/components/ai-chat/index.vue index c96cd39bc..5a48b655a 100644 --- a/ui/src/components/ai-chat/index.vue +++ b/ui/src/components/ai-chat/index.vue @@ -4,10 +4,8 @@
- - + +
@@ -52,10 +50,8 @@
- - + +
@@ -610,7 +606,7 @@ onMounted(() => { if (quickInputRef.value) { quickInputRef.value.textarea.style.height = '0' } - }, 1500) + }, 1800) }) defineExpose({ diff --git a/ui/src/components/layout-container/index.vue b/ui/src/components/layout-container/index.vue index 4d830242c..ec5d8c3b2 100644 --- a/ui/src/components/layout-container/index.vue +++ b/ui/src/components/layout-container/index.vue @@ -42,7 +42,7 @@ const showBack = computed(() => { .content-container__main { background-color: var(--app-view-bg-color); box-sizing: border-box; - min-width: 700px; + min-width: 847px; } } diff --git a/ui/src/layout/components/top-bar/avatar/AboutDialog.vue b/ui/src/layout/components/top-bar/avatar/AboutDialog.vue index e92eaa928..91badd322 100644 --- a/ui/src/layout/components/top-bar/avatar/AboutDialog.vue +++ b/ui/src/layout/components/top-bar/avatar/AboutDialog.vue @@ -26,11 +26,10 @@ >
- 版本{{ user.isXPack ? '专业版' : '社区版' }} + 版本{{ user.showXpack() ? '专业版' : '社区版' }}
- 版本号{{ licenseInfo?.licenseVersion || user.version }} + 版本号{{ user.version }}
序列号{{ licenseInfo?.serialNo || '-' }} @@ -38,11 +37,7 @@
备注{{ licenseInfo?.remark || '-' }}
- -
+
+ diff --git a/ui/src/views/application-overview/component/LimitDialog.vue b/ui/src/views/application-overview/component/LimitDialog.vue index 6bdffd2fd..2bb9ba501 100644 --- a/ui/src/views/application-overview/component/LimitDialog.vue +++ b/ui/src/views/application-overview/component/LimitDialog.vue @@ -4,12 +4,12 @@ v-model="dialogVisible" > - - + --> @@ -69,7 +69,6 @@ const emit = defineEmits(['refresh']) const limitFormRef = ref() const form = ref({ - show_source: false, access_num: 0, white_active: true, white_list: '' @@ -81,7 +80,6 @@ const loading = ref(false) watch(dialogVisible, (bool) => { if (!bool) { form.value = { - show_source: false, access_num: 0, white_active: true, white_list: '' @@ -90,7 +88,6 @@ watch(dialogVisible, (bool) => { }) const open = (data: any) => { - form.value.show_source = data.show_source 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') : '' @@ -102,7 +99,6 @@ const submit = async (formEl: FormInstance | undefined) => { await formEl.validate((valid, fields) => { if (valid) { const obj = { - show_source: form.value.show_source, white_list: form.value.white_list ? form.value.white_list.split('\n') : [], white_active: form.value.white_active, access_num: form.value.access_num diff --git a/ui/src/views/application-overview/index.vue b/ui/src/views/application-overview/index.vue index 9831832c4..ae4724efb 100644 --- a/ui/src/views/application-overview/index.vue +++ b/ui/src/views/application-overview/index.vue @@ -2,7 +2,9 @@
-

{{$t('views.applicationOverview.appInfo.header')}}

+

+ {{ $t('views.applicationOverview.appInfo.header') }} +

- {{$t('views.applicationOverview.appInfo.publicAccessLink')}} + {{ + $t('views.applicationOverview.appInfo.publicAccessLink') + }}
- {{$t('views.applicationOverview.appInfo.demo')}} - {{$t('views.applicationOverview.appInfo.demo')}} + + {{ $t('views.applicationOverview.appInfo.demo') }} + + {{ $t('views.applicationOverview.appInfo.demo') }} - {{$t('views.applicationOverview.appInfo.embedThirdParty')}} + {{ $t('views.applicationOverview.appInfo.embedThirdParty') }} + + + {{ $t('views.applicationOverview.appInfo.accessRestrictions') }} + + + {{ $t('views.applicationOverview.appInfo.displaySetting') }} - {{$t('views.applicationOverview.appInfo.accessRestrictions')}}
- {{$t('views.applicationOverview.appInfo.apiAccessCredentials')}} + {{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }} +
- {{$t('views.applicationOverview.appInfo.apiKey')}} + {{ + $t('views.applicationOverview.appInfo.apiKey') + }}
-

{{$t('views.applicationOverview.monitor.monitoringStatistics')}}

+

+ {{ $t('views.applicationOverview.monitor.monitoringStatistics') }} +

+ diff --git a/ui/src/views/chat/pc/index.vue b/ui/src/views/chat/pc/index.vue index 50416a4ab..6ccecbb9f 100644 --- a/ui/src/views/chat/pc/index.vue +++ b/ui/src/views/chat/pc/index.vue @@ -108,7 +108,6 @@ import { reactive, ref, onMounted, nextTick, computed } from 'vue' import { useRoute } from 'vue-router' import { marked } from 'marked' import { saveAs } from 'file-saver' -import applicationApi from '@/api/application' import useStore from '@/stores' import useResize from '@/layout/hooks/useResize' @@ -201,11 +200,14 @@ function getAccessToken(token: string) { } function getAppProfile() { - applicationApi - .getAppProfile(loading) - .then((res) => { + application + .asyncGetAppProfile(loading) + .then((res: any) => { applicationDetail.value = res.data - getChatLog(applicationDetail.value.id) + if (res.data?.show_history) { + getChatLog(applicationDetail.value.id) + } + }) .catch(() => { applicationAvailable.value = false diff --git a/ui/src/views/login/index.vue b/ui/src/views/login/index.vue index 115632bfc..e30ea0940 100644 --- a/ui/src/views/login/index.vue +++ b/ui/src/views/login/index.vue @@ -138,7 +138,7 @@ onMounted(() => { user.theme() } user.asyncGetProfile().then((res) => { - if (user.isXPack && user.XPACK_LICENSE_IS_VALID) { + if (user.showXpack()) { loading.value = true user .getAuthType() diff --git a/ui/src/views/theme/index.vue b/ui/src/views/theme/index.vue index 4d02b448d..f4186bdd5 100644 --- a/ui/src/views/theme/index.vue +++ b/ui/src/views/theme/index.vue @@ -134,7 +134,7 @@