mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-25 17:22:55 +00:00
feat: 显示设置功能
This commit is contained in:
parent
d7fb585bce
commit
5f3f1dd2ca
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -19,34 +19,8 @@ const guideHtml=`
|
|||
</div>
|
||||
`
|
||||
const chatButtonHtml=
|
||||
`<div class="maxkb-chat-button" ><svg style="vertical-align: middle;overflow: hidden;" width="48" height="56" viewBox="0 0 48 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_349_49711)">
|
||||
<path d="M8 24C8 12.9543 16.9543 4 28 4H48V44H28C16.9543 44 8 35.0457 8 24Z" fill="url(#paint0_linear_349_49711)"/>
|
||||
</g>
|
||||
<path d="M31.2632 30.2754H28.1992L27.0636 31.411C26.9971 31.4775 26.9518 31.5623 26.9335 31.6546C26.9151 31.7468 26.9245 31.8425 26.9605 31.9294C26.9965 32.0163 27.0575 32.0906 27.1357 32.1429C27.2139 32.1951 27.3059 32.223 27.4 32.223H32.0625C32.1566 32.223 32.2486 32.1951 32.3268 32.1429C32.405 32.0906 32.466 32.0163 32.502 31.9294C32.538 31.8425 32.5474 31.7468 32.529 31.6546C32.5107 31.5623 32.4654 31.4775 32.3989 31.411L31.2632 30.2754Z" fill="white"/>
|
||||
<path d="M39.6831 21.3652H39.0791V25.6142H39.6831C39.8051 25.6142 39.9221 25.5657 40.0083 25.4795C40.0945 25.3932 40.143 25.2763 40.143 25.1543V21.8251C40.143 21.7031 40.0945 21.5862 40.0083 21.4999C39.9221 21.4137 39.8051 21.3652 39.6831 21.3652Z" fill="white"/>
|
||||
<path d="M20.9208 21.3652H20.3168C20.1948 21.3652 20.0779 21.4137 19.9916 21.4999C19.9054 21.5862 19.8569 21.7031 19.8569 21.8251V25.1543C19.8569 25.2763 19.9054 25.3932 19.9916 25.4795C20.0779 25.5657 20.1948 25.6142 20.3168 25.6142H20.9208V21.3652Z" fill="white"/>
|
||||
<path d="M32.3323 21.9277C32.1041 21.9277 31.8854 22.0184 31.7241 22.1796C31.5628 22.3409 31.4722 22.5597 31.4722 22.7878V23.4045C31.4722 23.6326 31.5628 23.8514 31.7241 24.0127C31.8854 24.174 32.1042 24.2646 32.3323 24.2646C32.5604 24.2646 32.7792 24.174 32.9405 24.0127C33.1018 23.8514 33.1924 23.6326 33.1924 23.4045V22.7878C33.1924 22.6749 33.1702 22.563 33.1269 22.4587C33.0837 22.3543 33.0204 22.2595 32.9405 22.1796C32.8606 22.0998 32.7658 22.0364 32.6614 21.9932C32.5571 21.95 32.4452 21.9277 32.3323 21.9277Z" fill="white"/>
|
||||
<path d="M27.8464 21.9277C27.6183 21.9277 27.3995 22.0184 27.2382 22.1796C27.0769 22.3409 26.9863 22.5597 26.9863 22.7878V23.4045C26.9863 23.6326 27.0769 23.8514 27.2383 24.0127C27.3996 24.174 27.6183 24.2646 27.8465 24.2646C28.0746 24.2646 28.2933 24.174 28.4547 24.0127C28.616 23.8514 28.7066 23.6326 28.7066 23.4045V22.7878C28.7066 22.6749 28.6843 22.563 28.6411 22.4587C28.5979 22.3543 28.5345 22.2595 28.4546 22.1796C28.3748 22.0998 28.2799 22.0364 28.1756 21.9932C28.0712 21.95 27.9594 21.9277 27.8464 21.9277Z" fill="white"/>
|
||||
<path d="M35.2258 17.0488H24.7738C23.8508 17.0499 22.9659 17.417 22.3133 18.0696C21.6606 18.7223 21.2935 19.6071 21.2925 20.5301V26.4227C21.2935 27.3457 21.6606 28.2306 22.3133 28.8832C22.9659 29.5359 23.8508 29.903 24.7738 29.904H35.2258C36.1488 29.903 37.0336 29.5359 37.6863 28.8832C38.3389 28.2306 38.7061 27.3457 38.7071 26.4227V20.5301C38.7061 19.6071 38.3389 18.7223 37.6863 18.0696C37.0336 17.417 36.1488 17.0499 35.2258 17.0488ZM35.5181 26.3875C35.5181 26.5538 35.452 26.7133 35.3344 26.8309C35.2168 26.9485 35.0573 27.0146 34.891 27.0146H29.7929C29.0215 27.0146 28.2631 27.2129 27.5904 27.5903L25.8801 28.55V27.0146H25.1086C24.9422 27.0146 24.7827 26.9486 24.6651 26.831C24.5475 26.7134 24.4815 26.5539 24.4815 26.3876V20.1408C24.4815 19.9745 24.5475 19.815 24.6651 19.6974C24.7827 19.5798 24.9422 19.5137 25.1086 19.5137H34.891C35.0573 19.5137 35.2168 19.5798 35.3344 19.6974C35.452 19.815 35.5181 19.9745 35.5181 20.1408V26.3875Z" fill="white"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_349_49711" x="0" y="0" width="56" height="56" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="4"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.168627 0 0 0 0 0.372549 0 0 0 0 0.85098 0 0 0 0.24 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_349_49711"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_349_49711" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_349_49711" x1="48" y1="25.6667" x2="8" y2="25.6667" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#9258F7"/>
|
||||
<stop offset="1" stop-color="#3370FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
`<div class="maxkb-chat-button" >
|
||||
<img style="height:100%;width:100%;" src="{{float_icon}}">
|
||||
</div>`
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 {}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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<boolean>) => Promise<Result<any>> = (
|
||||
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<boolean>
|
||||
) => Promise<Result<any>> = (application_id, data, loading) => {
|
||||
return put(`${prefix}/${application_id}/setting`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话获取应用相关信息
|
||||
* @param 参数
|
||||
{
|
||||
"access_token": "string"
|
||||
}
|
||||
*/
|
||||
const getAppXpackProfile: (loading?: Ref<boolean>) => Promise<any> = (loading) => {
|
||||
return get(`${prefix}/xpack/profile`, undefined, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
getAccessToken,
|
||||
putAccessToken,
|
||||
getAppXpackProfile
|
||||
}
|
||||
|
|
@ -4,10 +4,8 @@
|
|||
<div ref="dialogScrollbar" class="ai-chat__content p-24 chat-width">
|
||||
<div class="item-content mb-16" v-if="!props.available || (props.data?.prologue && !log)">
|
||||
<div class="avatar">
|
||||
<!-- <AppAvatar class="avatar-gradient">
|
||||
<img src="@/assets/icon_robot.svg" style="width: 75%" alt="" />
|
||||
</AppAvatar> -->
|
||||
<LogoIcon height="30px" />
|
||||
<img v-if="data.avatar" :src="data.avatar" height="30px" />
|
||||
<LogoIcon v-else height="30px" />
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
|
@ -52,10 +50,8 @@
|
|||
<!-- 回答 -->
|
||||
<div class="item-content mb-16 lighter">
|
||||
<div class="avatar">
|
||||
<!-- <AppAvatar class="avatar-gradient">
|
||||
<img src="@/assets/icon_robot.svg" style="width: 75%" alt="" />
|
||||
</AppAvatar> -->
|
||||
<LogoIcon height="30px" />
|
||||
<img v-if="data.avatar" :src="data.avatar" height="30px" />
|
||||
<LogoIcon v-else height="30px" />
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
|
@ -610,7 +606,7 @@ onMounted(() => {
|
|||
if (quickInputRef.value) {
|
||||
quickInputRef.value.textarea.style.height = '0'
|
||||
}
|
||||
}, 1500)
|
||||
}, 1800)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -26,11 +26,10 @@
|
|||
>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="label">版本</span><span>{{ user.isXPack ? '专业版' : '社区版' }}</span>
|
||||
<span class="label">版本</span><span>{{ user.showXpack() ? '专业版' : '社区版' }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="label">版本号</span
|
||||
><span>{{ licenseInfo?.licenseVersion || user.version }}</span>
|
||||
<span class="label">版本号</span><span>{{ user.version }}</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="label">序列号</span><span>{{ licenseInfo?.serialNo || '-' }}</span>
|
||||
|
|
@ -38,11 +37,7 @@
|
|||
<div class="flex">
|
||||
<span class="label">备注</span><span>{{ licenseInfo?.remark || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-16 flex align-center"
|
||||
v-hasPermission="new ComplexPermission(['ADMIN'], ['x-pack'], 'OR')"
|
||||
>
|
||||
<div class="mt-16 flex align-center" v-if="user.showXpack()">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
action="#"
|
||||
|
|
@ -59,7 +54,7 @@
|
|||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import licenseApi from '@/api/license'
|
||||
import { fromNowDate } from '@/utils/time'
|
||||
import { ComplexPermission } from '@/utils/permission/type'
|
||||
|
|
@ -72,9 +67,18 @@ const isDefaultTheme = computed(() => {
|
|||
const aboutDialogVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const licenseInfo = ref<any>(null)
|
||||
const isUpdate = ref(false)
|
||||
|
||||
watch(aboutDialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
if (isUpdate.value) {
|
||||
window.location.reload()
|
||||
}
|
||||
isUpdate.value = false
|
||||
}
|
||||
})
|
||||
const open = () => {
|
||||
if (user.isXPack) {
|
||||
if (user.showXpack()) {
|
||||
getLicenseInfo()
|
||||
}
|
||||
|
||||
|
|
@ -86,6 +90,7 @@ const onChange = (file: any) => {
|
|||
fd.append('license_file', file.raw)
|
||||
licenseApi.putLicense(fd, loading).then((res: any) => {
|
||||
getLicenseInfo()
|
||||
isUpdate.value = true
|
||||
})
|
||||
}
|
||||
function getLicenseInfo() {
|
||||
|
|
|
|||
|
|
@ -1,103 +1,108 @@
|
|||
export default {
|
||||
title: 'Overview',
|
||||
appInfo: {
|
||||
header: 'Application Info',
|
||||
publicAccessLink: 'Public Access Link',
|
||||
openText: 'On',
|
||||
closeText: 'Off',
|
||||
copyLinkText: 'Copy Link',
|
||||
refreshLinkText: 'Refresh Link',
|
||||
demo: 'Demo',
|
||||
embedThirdParty: 'Embed Third Party',
|
||||
accessRestrictions: 'Access Restrictions',
|
||||
apiAccessCredentials: 'API Access Credentials',
|
||||
apiKey: 'API Key',
|
||||
refreshToken: {
|
||||
msgConfirm1: 'Do you want to regenerate the public access link?',
|
||||
msgConfirm2: 'Regenerating the public access link will affect third-party embedded scripts changes and will require re-embedding the new script into third-party sites. Please proceed with caution!',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
refreshSuccess: 'Refresh Successful',
|
||||
},
|
||||
changeState: {
|
||||
enableSuccess: 'Enable Successful',
|
||||
disableSuccess: 'Disable Successful',
|
||||
},
|
||||
APIKeyDialog: {
|
||||
creatApiKey: 'Create',
|
||||
status: 'Status',
|
||||
creationDate: 'Creation Date',
|
||||
operations: 'Operations',
|
||||
settings: 'Settings',
|
||||
delete: 'Delete',
|
||||
saveSettings: 'Save Settings',
|
||||
msgConfirm1: 'Are you sure you want to delete the API Key?',
|
||||
msgConfirm2: 'Deleting the API Key cannot be undone. Please confirm if you want to delete it!',
|
||||
confirmDelete: 'Delete',
|
||||
deleteSuccess: 'Delete Successful',
|
||||
cancel: 'Cancel',
|
||||
enabledSuccess: 'Enabled',
|
||||
disabledSuccess: 'Disabled',
|
||||
},
|
||||
EditAvatarDialog: {
|
||||
title: 'Edit Logo',
|
||||
customizeUpload: 'Custom Upload',
|
||||
upload: 'Upload',
|
||||
default: 'Default Logo',
|
||||
custom: 'Custom',
|
||||
sizeTip: 'Suggested size 32*32, supports ico, png, size no more than 200KB',
|
||||
cancel: 'Cancel',
|
||||
save: 'Save',
|
||||
fileSizeExceeded: 'File size exceeds 200KB',
|
||||
setSuccess: 'Setting Successful',
|
||||
uploadImagePrompt: 'Please upload an image'
|
||||
},
|
||||
EmbedDialog: {
|
||||
embedDialogTitle: 'Embed Third Party',
|
||||
fullscreenModeTitle: 'Fullscreen Mode',
|
||||
copyInstructions: 'Copy the following code to embed',
|
||||
floatingModeTitle: 'Floating Mode'
|
||||
},
|
||||
LimitDialog: {
|
||||
dialogTitle: 'Access Restrictions',
|
||||
showSourceLabel: 'Show Source',
|
||||
clientQueryLimitLabel: 'Each Client Query Limit',
|
||||
timesDays: 'Times/Day',
|
||||
whitelistLabel: 'Whitelist',
|
||||
whitelistPlaceholder: 'Please enter allowed third-party source addresses, one per line, such as:\nhttp://127.0.0.1:5678\nhttps://dataease.io',
|
||||
cancelButtonText: 'Cancel',
|
||||
saveButtonText: 'Save',
|
||||
settingSuccessMessage: 'Setting Successful'
|
||||
},
|
||||
SettingAPIKeyDialog: {
|
||||
dialogTitle: 'Settings',
|
||||
allowCrossDomainLabel: 'Allow Cross-Domain Address',
|
||||
crossDomainPlaceholder: 'Please enter allowed cross-domain addresses, if open without inputting addresses, there are no restrictions.\nCross-domain addresses one per line, such as:\nhttp://127.0.0.1:5678\nhttps://dataease.io',
|
||||
cancelButtonText: 'Cancel',
|
||||
saveButtonText: 'Save',
|
||||
successMessage: 'Setting Successful'
|
||||
}
|
||||
title: 'Overview',
|
||||
appInfo: {
|
||||
header: 'Application Info',
|
||||
publicAccessLink: 'Public Access Link',
|
||||
openText: 'On',
|
||||
closeText: 'Off',
|
||||
copyLinkText: 'Copy Link',
|
||||
refreshLinkText: 'Refresh Link',
|
||||
demo: 'Demo',
|
||||
embedThirdParty: 'Embed Third Party',
|
||||
accessRestrictions: 'Access Restrictions',
|
||||
displaySetting: 'Display Setting',
|
||||
apiAccessCredentials: 'API Access Credentials',
|
||||
apiKey: 'API Key',
|
||||
refreshToken: {
|
||||
msgConfirm1: 'Do you want to regenerate the public access link?',
|
||||
msgConfirm2:
|
||||
'Regenerating the public access link will affect third-party embedded scripts changes and will require re-embedding the new script into third-party sites. Please proceed with caution!',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
refreshSuccess: 'Refresh Successful'
|
||||
},
|
||||
monitor: {
|
||||
monitoringStatistics: 'Monitoring Statistics',
|
||||
customRange: 'Custom Range',
|
||||
startDatePlaceholder: 'Start Date',
|
||||
endDatePlaceholder: 'End Date',
|
||||
pastDayOptions: {
|
||||
past7Days: 'Past 7 Days',
|
||||
past30Days: 'Past 30 Days',
|
||||
past90Days: 'Past 90 Days',
|
||||
past183Days: 'Past Half Year',
|
||||
other: 'Custom'
|
||||
},
|
||||
charts: {
|
||||
customerTotal: 'Total Customers',
|
||||
customerNew: 'New Customers',
|
||||
queryCount: 'Query Count',
|
||||
tokensTotal: 'Total Tokens',
|
||||
userSatisfaction: 'User Satisfaction',
|
||||
approval: 'Approval',
|
||||
disapproval: 'Disapproval'
|
||||
}
|
||||
changeState: {
|
||||
enableSuccess: 'Enable Successful',
|
||||
disableSuccess: 'Disable Successful'
|
||||
},
|
||||
};
|
||||
APIKeyDialog: {
|
||||
creatApiKey: 'Create',
|
||||
status: 'Status',
|
||||
creationDate: 'Creation Date',
|
||||
operations: 'Operations',
|
||||
settings: 'Settings',
|
||||
delete: 'Delete',
|
||||
saveSettings: 'Save Settings',
|
||||
msgConfirm1: 'Are you sure you want to delete the API Key?',
|
||||
msgConfirm2:
|
||||
'Deleting the API Key cannot be undone. Please confirm if you want to delete it!',
|
||||
confirmDelete: 'Delete',
|
||||
deleteSuccess: 'Delete Successful',
|
||||
cancel: 'Cancel',
|
||||
enabledSuccess: 'Enabled',
|
||||
disabledSuccess: 'Disabled'
|
||||
},
|
||||
EditAvatarDialog: {
|
||||
title: 'Edit Logo',
|
||||
customizeUpload: 'Custom Upload',
|
||||
upload: 'Upload',
|
||||
default: 'Default Logo',
|
||||
custom: 'Custom',
|
||||
sizeTip: 'Suggested size 32*32, supports ico, png, size no more than 200KB',
|
||||
cancel: 'Cancel',
|
||||
save: 'Save',
|
||||
fileSizeExceeded: 'File size exceeds 200KB',
|
||||
setSuccess: 'Setting Successful',
|
||||
uploadImagePrompt: 'Please upload an image'
|
||||
},
|
||||
EmbedDialog: {
|
||||
embedDialogTitle: 'Embed Third Party',
|
||||
fullscreenModeTitle: 'Fullscreen Mode',
|
||||
copyInstructions: 'Copy the following code to embed',
|
||||
floatingModeTitle: 'Floating Mode'
|
||||
},
|
||||
LimitDialog: {
|
||||
dialogTitle: 'Access Restrictions',
|
||||
showSourceLabel: 'Show Source',
|
||||
clientQueryLimitLabel: 'Each Client Query Limit',
|
||||
timesDays: 'Times/Day',
|
||||
whitelistLabel: 'Whitelist',
|
||||
whitelistPlaceholder:
|
||||
'Please enter allowed third-party source addresses, one per line, such as:\nhttp://127.0.0.1:5678\nhttps://dataease.io',
|
||||
cancelButtonText: 'Cancel',
|
||||
saveButtonText: 'Save',
|
||||
settingSuccessMessage: 'Setting Successful'
|
||||
},
|
||||
SettingAPIKeyDialog: {
|
||||
dialogTitle: 'Settings',
|
||||
allowCrossDomainLabel: 'Allow Cross-Domain Address',
|
||||
crossDomainPlaceholder:
|
||||
'Please enter allowed cross-domain addresses, if open without inputting addresses, there are no restrictions.\nCross-domain addresses one per line, such as:\nhttp://127.0.0.1:5678\nhttps://dataease.io',
|
||||
cancelButtonText: 'Cancel',
|
||||
saveButtonText: 'Save',
|
||||
successMessage: 'Setting Successful'
|
||||
}
|
||||
},
|
||||
monitor: {
|
||||
monitoringStatistics: 'Monitoring Statistics',
|
||||
customRange: 'Custom Range',
|
||||
startDatePlaceholder: 'Start Date',
|
||||
endDatePlaceholder: 'End Date',
|
||||
pastDayOptions: {
|
||||
past7Days: 'Past 7 Days',
|
||||
past30Days: 'Past 30 Days',
|
||||
past90Days: 'Past 90 Days',
|
||||
past183Days: 'Past Half Year',
|
||||
other: 'Custom'
|
||||
},
|
||||
charts: {
|
||||
customerTotal: 'Total Customers',
|
||||
customerNew: 'New Customers',
|
||||
queryCount: 'Query Count',
|
||||
tokensTotal: 'Total Tokens',
|
||||
userSatisfaction: 'User Satisfaction',
|
||||
approval: 'Approval',
|
||||
disapproval: 'Disapproval'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,103 +1,107 @@
|
|||
export default {
|
||||
title: '概览',
|
||||
appInfo:{
|
||||
header: '应用信息',
|
||||
publicAccessLink: '公开访问链接',
|
||||
openText: '开',
|
||||
closeText: '关',
|
||||
copyLinkText: '复制链接',
|
||||
refreshLinkText: '刷新链接',
|
||||
demo: '演示',
|
||||
embedThirdParty: '嵌入第三方',
|
||||
accessRestrictions: '访问限制',
|
||||
apiAccessCredentials: 'API访问凭据',
|
||||
apiKey: 'API Key',
|
||||
refreshToken:{
|
||||
msgConfirm1:'是否重新生成公开访问链接?',
|
||||
msgConfirm2:'重新生成公开访问链接会影响嵌入第三方脚本变更,需要将新脚本重新嵌入第三方,请谨慎操作!',
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
refreshSuccess: '刷新成功',
|
||||
},
|
||||
changeState:{
|
||||
enableSuccess: '启用成功',
|
||||
disableSuccess: '禁用成功',
|
||||
},
|
||||
APIKeyDialog:{
|
||||
creatApiKey: '创建',
|
||||
status: '状态',
|
||||
creationDate: '创建日期',
|
||||
operations: '操作',
|
||||
settings: '设置',
|
||||
delete: '删除',
|
||||
saveSettings: '保存设置',
|
||||
msgConfirm1: '是否删除API Key?',
|
||||
msgConfirm2: '删除API Key后将无法恢复,请确认是否删除!',
|
||||
confirmDelete: '删除',
|
||||
deleteSuccess: '删除成功',
|
||||
cancel: '取消',
|
||||
enabledSuccess: '已启用',
|
||||
disabledSuccess: '已禁用',
|
||||
},
|
||||
EditAvatarDialog:{
|
||||
title: '编辑logo',
|
||||
customizeUpload: '自定义上传',
|
||||
upload: '上传',
|
||||
default: '默认logo',
|
||||
custom: '自定义',
|
||||
sizeTip: '建议尺寸 32*32,支持 ico、png,大小不超过200KB',
|
||||
cancel: '取消',
|
||||
save: '保存',
|
||||
fileSizeExceeded: '文件大小超过 200KB',
|
||||
setSuccess: '设置成功',
|
||||
uploadImagePrompt: '请上传一张图片'
|
||||
},
|
||||
EmbedDialog:{
|
||||
embedDialogTitle: '嵌入第三方',
|
||||
fullscreenModeTitle: '全屏模式',
|
||||
copyInstructions: '复制以下代码进行嵌入',
|
||||
floatingModeTitle: '浮窗模式'
|
||||
},
|
||||
LimitDialog:{
|
||||
dialogTitle: '访问限制',
|
||||
showSourceLabel: '显示知识来源',
|
||||
clientQueryLimitLabel: '每个客户端提问限制',
|
||||
timesDays: '次/天',
|
||||
whitelistLabel: '白名单',
|
||||
whitelistPlaceholder: '请输入允许嵌入第三方的源地址,一行一个,如:\nhttp://127.0.0.1:5678\nhttps://dataease.io',
|
||||
cancelButtonText: '取消',
|
||||
saveButtonText: '保存',
|
||||
settingSuccessMessage: '设置成功'
|
||||
},
|
||||
SettingAPIKeyDialog:{
|
||||
dialogTitle: '设置',
|
||||
allowCrossDomainLabel: '允许跨域地址',
|
||||
crossDomainPlaceholder: '请输入允许的跨域地址,开启后不输入跨域地址则不限制。\n跨域地址一行一个,如:\nhttp://127.0.0.1:5678 \nhttps://dataease.io',
|
||||
cancelButtonText: '取消',
|
||||
saveButtonText: '保存',
|
||||
successMessage: '设置成功'
|
||||
}
|
||||
title: '概览',
|
||||
appInfo: {
|
||||
header: '应用信息',
|
||||
publicAccessLink: '公开访问链接',
|
||||
openText: '开',
|
||||
closeText: '关',
|
||||
copyLinkText: '复制链接',
|
||||
refreshLinkText: '刷新链接',
|
||||
demo: '演示',
|
||||
embedThirdParty: '嵌入第三方',
|
||||
accessRestrictions: '访问限制',
|
||||
displaySetting: '显示设置',
|
||||
apiAccessCredentials: 'API访问凭据',
|
||||
apiKey: 'API Key',
|
||||
refreshToken: {
|
||||
msgConfirm1: '是否重新生成公开访问链接?',
|
||||
msgConfirm2:
|
||||
'重新生成公开访问链接会影响嵌入第三方脚本变更,需要将新脚本重新嵌入第三方,请谨慎操作!',
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
refreshSuccess: '刷新成功'
|
||||
},
|
||||
monitor:{
|
||||
monitoringStatistics: '监控统计',
|
||||
customRange: '自定义范围',
|
||||
startDatePlaceholder: '开始时间',
|
||||
endDatePlaceholder: '结束时间',
|
||||
pastDayOptions:{
|
||||
past7Days:'过去7天',
|
||||
past30Days:'过去30天',
|
||||
past90Days:'过去90天',
|
||||
past183Days:'过去半年',
|
||||
other:'自定义'
|
||||
},
|
||||
charts:{
|
||||
'customerTotal': '用户总数',
|
||||
'customerNew': '用户新增数',
|
||||
'queryCount': '提问次数',
|
||||
'tokensTotal': 'Tokens 总数',
|
||||
'userSatisfaction': '用户满意度',
|
||||
'approval': '赞同',
|
||||
'disapproval': '反对'
|
||||
}
|
||||
changeState: {
|
||||
enableSuccess: '启用成功',
|
||||
disableSuccess: '禁用成功'
|
||||
},
|
||||
};
|
||||
APIKeyDialog: {
|
||||
creatApiKey: '创建',
|
||||
status: '状态',
|
||||
creationDate: '创建日期',
|
||||
operations: '操作',
|
||||
settings: '设置',
|
||||
delete: '删除',
|
||||
saveSettings: '保存设置',
|
||||
msgConfirm1: '是否删除API Key?',
|
||||
msgConfirm2: '删除API Key后将无法恢复,请确认是否删除!',
|
||||
confirmDelete: '删除',
|
||||
deleteSuccess: '删除成功',
|
||||
cancel: '取消',
|
||||
enabledSuccess: '已启用',
|
||||
disabledSuccess: '已禁用'
|
||||
},
|
||||
EditAvatarDialog: {
|
||||
title: '编辑logo',
|
||||
customizeUpload: '自定义上传',
|
||||
upload: '上传',
|
||||
default: '默认logo',
|
||||
custom: '自定义',
|
||||
sizeTip: '建议尺寸 32*32,支持 ico、png,大小不超过200KB',
|
||||
cancel: '取消',
|
||||
save: '保存',
|
||||
fileSizeExceeded: '文件大小超过 200KB',
|
||||
setSuccess: '设置成功',
|
||||
uploadImagePrompt: '请上传一张图片'
|
||||
},
|
||||
EmbedDialog: {
|
||||
embedDialogTitle: '嵌入第三方',
|
||||
fullscreenModeTitle: '全屏模式',
|
||||
copyInstructions: '复制以下代码进行嵌入',
|
||||
floatingModeTitle: '浮窗模式'
|
||||
},
|
||||
LimitDialog: {
|
||||
dialogTitle: '访问限制',
|
||||
showSourceLabel: '显示知识来源',
|
||||
clientQueryLimitLabel: '每个客户端提问限制',
|
||||
timesDays: '次/天',
|
||||
whitelistLabel: '白名单',
|
||||
whitelistPlaceholder:
|
||||
'请输入允许嵌入第三方的源地址,一行一个,如:\nhttp://127.0.0.1:5678\nhttps://dataease.io',
|
||||
cancelButtonText: '取消',
|
||||
saveButtonText: '保存',
|
||||
settingSuccessMessage: '设置成功'
|
||||
},
|
||||
SettingAPIKeyDialog: {
|
||||
dialogTitle: '设置',
|
||||
allowCrossDomainLabel: '允许跨域地址',
|
||||
crossDomainPlaceholder:
|
||||
'请输入允许的跨域地址,开启后不输入跨域地址则不限制。\n跨域地址一行一个,如:\nhttp://127.0.0.1:5678 \nhttps://dataease.io',
|
||||
cancelButtonText: '取消',
|
||||
saveButtonText: '保存',
|
||||
successMessage: '设置成功'
|
||||
}
|
||||
},
|
||||
monitor: {
|
||||
monitoringStatistics: '监控统计',
|
||||
customRange: '自定义范围',
|
||||
startDatePlaceholder: '开始时间',
|
||||
endDatePlaceholder: '结束时间',
|
||||
pastDayOptions: {
|
||||
past7Days: '过去7天',
|
||||
past30Days: '过去30天',
|
||||
past90Days: '过去90天',
|
||||
past183Days: '过去半年',
|
||||
other: '自定义'
|
||||
},
|
||||
charts: {
|
||||
customerTotal: '用户总数',
|
||||
customerNew: '用户新增数',
|
||||
queryCount: '提问次数',
|
||||
tokensTotal: 'Tokens 总数',
|
||||
userSatisfaction: '用户满意度',
|
||||
approval: '赞同',
|
||||
disapproval: '反对'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import applicationApi from '@/api/application'
|
||||
import applicationXpackApi from '@/api/application-xpack'
|
||||
import { type Ref } from 'vue'
|
||||
|
||||
import useUserStore from './user'
|
||||
|
||||
const useApplicationStore = defineStore({
|
||||
id: 'application',
|
||||
state: () => ({
|
||||
|
|
@ -49,14 +52,51 @@ const useApplicationStore = defineStore({
|
|||
|
||||
async asyncGetAccessToken(id: string, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
applicationApi
|
||||
.getAccessToken(id, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
const user = useUserStore()
|
||||
if (user.isEnterprise()) {
|
||||
applicationXpackApi
|
||||
.getAccessToken(id, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
} else {
|
||||
applicationApi
|
||||
.getAccessToken(id, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async asyncGetAppProfile(loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const user = useUserStore()
|
||||
if (user.isEnterprise()) {
|
||||
applicationXpackApi
|
||||
.getAppXpackProfile(loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
} else {
|
||||
applicationApi
|
||||
.getAppProfile(loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ const useUserStore = defineStore({
|
|||
themeInfo: null
|
||||
}),
|
||||
actions: {
|
||||
showXpack() {
|
||||
return this.isXPack
|
||||
},
|
||||
isDefaultTheme() {
|
||||
return !this.themeInfo?.theme || this.themeInfo?.theme === '#3370FF'
|
||||
},
|
||||
|
|
@ -98,10 +101,10 @@ const useUserStore = defineStore({
|
|||
return await ThemeApi.getThemeInfo().then((ok) => {
|
||||
this.setTheme(ok.data)
|
||||
window.document.title = this.themeInfo['title'] || 'MaxKB'
|
||||
const link = document.querySelector('link[rel="icon"]') as any
|
||||
if (link) {
|
||||
link['href'] = this.themeInfo['icon'] || '/favicon.ico'
|
||||
}
|
||||
// const link = document.querySelector('link[rel="icon"]') as any
|
||||
// if (link) {
|
||||
// link['href'] = this.themeInfo['icon'] || '/favicon.ico'
|
||||
// }
|
||||
})
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,213 @@
|
|||
<template>
|
||||
<el-dialog title="显示设置" v-model="dialogVisible">
|
||||
<el-form label-position="top" ref="displayFormRef" :model="form">
|
||||
<el-form-item>
|
||||
<el-space direction="vertical" alignment="start">
|
||||
<el-checkbox v-model="form.show_source" label="显示知识来源" />
|
||||
<el-checkbox v-model="form.show_history" label="显示历史记录" />
|
||||
<el-checkbox v-model="form.draggable" label="可拖拽位置(浮窗模式)" />
|
||||
<el-checkbox v-model="form.show_guide" label="显示引导图(浮窗模式)" />
|
||||
</el-space>
|
||||
</el-form-item>
|
||||
<el-form-item label="对话头像">
|
||||
<div class="flex mt-8">
|
||||
<div class="border border-r-4 mr-16" style="width: 50px; height: 50px; padding: 8px">
|
||||
<img v-if="imgUrl.avatar" :src="imgUrl.avatar" alt="" height="50px" />
|
||||
<LogoIcon v-else height="50px" />
|
||||
</div>
|
||||
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept="image/*"
|
||||
:on-change="(file: any, fileList: any) => onChange(file, fileList, 'avatar')"
|
||||
>
|
||||
<el-button icon="Upload">{{
|
||||
$t('views.applicationOverview.appInfo.EditAvatarDialog.upload')
|
||||
}}</el-button>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip info" style="margin-top: 0">
|
||||
建议尺寸 32*32,支持 JPG、PNG,大小不超过 200 KB
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="浮窗入口图标">
|
||||
<div class="flex mt-8">
|
||||
<div class="border border-r-4 mr-16" style="width: 50px; height: 50px; padding: 8px">
|
||||
<img v-if="imgUrl.float_icon" :src="imgUrl.float_icon" alt="" height="50px" />
|
||||
<img v-else src="@/assets/logo/logo.svg" height="50px" />
|
||||
</div>
|
||||
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept="image/*"
|
||||
:on-change="(file: any, fileList: any) => onChange(file, fileList, 'float_icon')"
|
||||
>
|
||||
<el-button icon="Upload">{{
|
||||
$t('views.applicationOverview.appInfo.EditAvatarDialog.upload')
|
||||
}}</el-button>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip info" style="margin-top: 0">
|
||||
建议尺寸 32*32,支持 JPG、PNG,大小不超过 200 KB
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button v-if="user.isEnterprise()" type="primary" @click.prevent="resetForm" link
|
||||
>恢复默认
|
||||
</el-button>
|
||||
<el-button @click.prevent="dialogVisible = false"
|
||||
>{{ $t('views.applicationOverview.appInfo.LimitDialog.cancelButtonText') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submit(displayFormRef)" :loading="loading">
|
||||
{{ $t('views.applicationOverview.appInfo.LimitDialog.saveButtonText') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { FormInstance, FormRules, UploadFiles } from 'element-plus'
|
||||
import applicationApi from '@/api/application'
|
||||
import applicationXpackApi from '@/api/application-xpack'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
import useStore from '@/stores'
|
||||
const { user } = useStore()
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }
|
||||
} = route
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const defaultSetting = {
|
||||
show_source: true,
|
||||
show_history: true,
|
||||
draggable: false,
|
||||
show_guide: true,
|
||||
avatar: '',
|
||||
float_icon: ''
|
||||
}
|
||||
|
||||
const displayFormRef = ref()
|
||||
const form = ref<any>({
|
||||
show_source: false
|
||||
})
|
||||
|
||||
const xpackForm = ref<any>({
|
||||
show_source: false,
|
||||
show_history: false,
|
||||
draggable: false,
|
||||
show_guide: false,
|
||||
avatar: '',
|
||||
float_icon: ''
|
||||
})
|
||||
|
||||
const imgUrl = ref<any>({
|
||||
avatar: '',
|
||||
float_icon: ''
|
||||
})
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const loading = ref(false)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
form.value = {
|
||||
show_source: false
|
||||
}
|
||||
imgUrl.value = {
|
||||
avatar: "",
|
||||
float_icon: ""
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function resetForm() {
|
||||
form.value = {
|
||||
...defaultSetting
|
||||
}
|
||||
imgUrl.value = {
|
||||
avatar: "",
|
||||
float_icon: ""
|
||||
}
|
||||
}
|
||||
|
||||
const onChange = (file: any, fileList: UploadFiles, attr: string) => {
|
||||
//1、判断文件大小是否合法,文件限制不能大于 200KB
|
||||
const isLimit = file?.size / 1024 < 200
|
||||
if (!isLimit) {
|
||||
// @ts-ignore
|
||||
MsgError(t('views.applicationOverview.appInfo.EditAvatarDialog.fileSizeExceeded'))
|
||||
return false
|
||||
}
|
||||
xpackForm.value[attr] = file.raw
|
||||
imgUrl.value[attr] = URL.createObjectURL(file.raw)
|
||||
}
|
||||
|
||||
const open = (data: any) => {
|
||||
if (user.isEnterprise()) {
|
||||
xpackForm.value.show_source = data.show_source
|
||||
xpackForm.value.show_history = data.show_history
|
||||
xpackForm.value.draggable = data.draggable
|
||||
xpackForm.value.show_guide = data.show_guide
|
||||
xpackForm.value.avatar = data.avatar
|
||||
xpackForm.value.float_icon = data.float_icon
|
||||
imgUrl.value.avatar = data.avatar
|
||||
imgUrl.value.float_icon = data.float_icon
|
||||
form.value = xpackForm.value
|
||||
} else {
|
||||
form.value.show_source = data.show_source
|
||||
}
|
||||
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
if (user.isEnterprise()) {
|
||||
let fd = new FormData()
|
||||
Object.keys(form.value).map((item) => {
|
||||
fd.append(item, form.value[item])
|
||||
})
|
||||
applicationXpackApi.putAccessToken(id as string, fd, loading).then((res) => {
|
||||
emit('refresh')
|
||||
// @ts-ignore
|
||||
MsgSuccess(t('views.applicationOverview.appInfo.LimitDialog.settingSuccessMessage'))
|
||||
dialogVisible.value = false
|
||||
})
|
||||
} else {
|
||||
const obj = {
|
||||
show_source: form.value.show_source
|
||||
}
|
||||
applicationApi.putAccessToken(id as string, obj, loading).then((res) => {
|
||||
emit('refresh')
|
||||
// @ts-ignore
|
||||
MsgSuccess(t('views.applicationOverview.appInfo.LimitDialog.settingSuccessMessage'))
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scope></style>
|
||||
|
|
@ -4,12 +4,12 @@
|
|||
v-model="dialogVisible"
|
||||
>
|
||||
<el-form label-position="top" ref="limitFormRef" :model="form">
|
||||
<el-form-item
|
||||
<!-- <el-form-item
|
||||
:label="$t('views.applicationOverview.appInfo.LimitDialog.showSourceLabel')"
|
||||
@click.prevent
|
||||
>
|
||||
<el-switch size="small" v-model="form.show_source"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item
|
||||
:label="$t('views.applicationOverview.appInfo.LimitDialog.clientQueryLimitLabel')"
|
||||
>
|
||||
|
|
@ -69,7 +69,6 @@ const emit = defineEmits(['refresh'])
|
|||
|
||||
const limitFormRef = ref()
|
||||
const form = ref<any>({
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
<LayoutContainer :header="$t('views.applicationOverview.title')">
|
||||
<el-scrollbar>
|
||||
<div class="main-calc-height p-24">
|
||||
<h4 class="title-decoration-1 mb-16">{{$t('views.applicationOverview.appInfo.header')}}</h4>
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('views.applicationOverview.appInfo.header') }}
|
||||
</h4>
|
||||
<el-card shadow="never" class="overview-card" v-loading="loading">
|
||||
<div class="title flex align-center">
|
||||
<div
|
||||
|
|
@ -42,7 +44,9 @@
|
|||
<el-row :gutter="12">
|
||||
<el-col :span="12" class="mt-16">
|
||||
<div class="flex">
|
||||
<el-text type="info">{{$t('views.applicationOverview.appInfo.publicAccessLink')}}</el-text>
|
||||
<el-text type="info">{{
|
||||
$t('views.applicationOverview.appInfo.publicAccessLink')
|
||||
}}</el-text>
|
||||
<el-switch
|
||||
v-model="accessToken.is_active"
|
||||
class="ml-8"
|
||||
|
|
@ -68,18 +72,27 @@
|
|||
</div>
|
||||
<div>
|
||||
<el-button :disabled="!accessToken?.is_active" type="primary">
|
||||
<a v-if="accessToken?.is_active" :href="shareUrl" target="_blank"> {{$t('views.applicationOverview.appInfo.demo')}} </a>
|
||||
<span v-else> {{$t('views.applicationOverview.appInfo.demo')}}</span>
|
||||
<a v-if="accessToken?.is_active" :href="shareUrl" target="_blank">
|
||||
{{ $t('views.applicationOverview.appInfo.demo') }}
|
||||
</a>
|
||||
<span v-else> {{ $t('views.applicationOverview.appInfo.demo') }}</span>
|
||||
</el-button>
|
||||
<el-button :disabled="!accessToken?.is_active" @click="openDialog">
|
||||
{{$t('views.applicationOverview.appInfo.embedThirdParty')}}
|
||||
{{ $t('views.applicationOverview.appInfo.embedThirdParty') }}
|
||||
</el-button>
|
||||
<el-button @click="openLimitDialog">
|
||||
{{ $t('views.applicationOverview.appInfo.accessRestrictions') }}
|
||||
</el-button>
|
||||
<el-button @click="openDisplaySettingDialog">
|
||||
{{ $t('views.applicationOverview.appInfo.displaySetting') }}
|
||||
</el-button>
|
||||
<el-button @click="openLimitDialog"> {{$t('views.applicationOverview.appInfo.accessRestrictions')}} </el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mt-16">
|
||||
<div class="flex">
|
||||
<el-text type="info">{{$t('views.applicationOverview.appInfo.apiAccessCredentials')}} </el-text>
|
||||
<el-text type="info"
|
||||
>{{ $t('views.applicationOverview.appInfo.apiAccessCredentials') }}
|
||||
</el-text>
|
||||
</div>
|
||||
<div class="mt-4 mb-16 url-height">
|
||||
<a target="_blank" :href="apiUrl" class="vertical-middle lighter break-all">
|
||||
|
|
@ -91,12 +104,16 @@
|
|||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="openAPIKeyDialog">{{$t('views.applicationOverview.appInfo.apiKey')}}</el-button>
|
||||
<el-button @click="openAPIKeyDialog">{{
|
||||
$t('views.applicationOverview.appInfo.apiKey')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<h4 class="title-decoration-1 mt-16 mb-16">{{$t('views.applicationOverview.monitor.monitoringStatistics')}}</h4>
|
||||
<h4 class="title-decoration-1 mt-16 mb-16">
|
||||
{{ $t('views.applicationOverview.monitor.monitoringStatistics') }}
|
||||
</h4>
|
||||
<div class="mb-16">
|
||||
<el-select v-model="history_day" class="mr-12 w-120" @change="changeDayHandle">
|
||||
<el-option
|
||||
|
|
@ -126,6 +143,7 @@
|
|||
<APIKeyDialog ref="APIKeyDialogRef" />
|
||||
<LimitDialog ref="LimitDialogRef" @refresh="refresh" />
|
||||
<EditAvatarDialog ref="EditAvatarDialogRef" @refresh="refreshIcon" />
|
||||
<DisplaySettingDialog ref="DisplaySettingDialogRef" @refresh="refresh" />
|
||||
</LayoutContainer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
|
@ -134,6 +152,8 @@ import { useRoute } from 'vue-router'
|
|||
import EmbedDialog from './component/EmbedDialog.vue'
|
||||
import APIKeyDialog from './component/APIKeyDialog.vue'
|
||||
import LimitDialog from './component/LimitDialog.vue'
|
||||
import DisplaySettingDialog from './component/DisplaySettingDialog.vue'
|
||||
|
||||
import EditAvatarDialog from './component/EditAvatarDialog.vue'
|
||||
import StatisticsCharts from './component/StatisticsCharts.vue'
|
||||
import applicationApi from '@/api/application'
|
||||
|
|
@ -152,6 +172,7 @@ const {
|
|||
|
||||
const apiUrl = window.location.origin + '/doc/chat/'
|
||||
|
||||
const DisplaySettingDialogRef = ref()
|
||||
const EditAvatarDialogRef = ref()
|
||||
const LimitDialogRef = ref()
|
||||
const APIKeyDialogRef = ref()
|
||||
|
|
@ -168,7 +189,7 @@ const dayOptions = [
|
|||
{
|
||||
value: 7,
|
||||
// @ts-ignore
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past7Days') // 使用 t 方法来国际化显示文本
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past7Days') // 使用 t 方法来国际化显示文本
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
|
|
@ -204,6 +225,9 @@ const statisticsData = ref([])
|
|||
|
||||
const showEditIcon = ref(false)
|
||||
|
||||
function openDisplaySettingDialog() {
|
||||
DisplaySettingDialogRef.value.open(accessToken.value)
|
||||
}
|
||||
function openEditAvatar() {
|
||||
EditAvatarDialogRef.value.open(detail.value)
|
||||
}
|
||||
|
|
@ -234,14 +258,14 @@ function refreshAccessToken() {
|
|||
t('views.applicationOverview.appInfo.refreshToken.msgConfirm2'),
|
||||
{
|
||||
confirmButtonText: t('views.applicationOverview.appInfo.refreshToken.confirm'),
|
||||
cancelButtonText:t('views.applicationOverview.appInfo.refreshToken.cancel')
|
||||
cancelButtonText: t('views.applicationOverview.appInfo.refreshToken.cancel')
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
const obj = {
|
||||
access_token_reset: true
|
||||
}
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
const str = t('views.applicationOverview.appInfo.refreshToken.refreshSuccess')
|
||||
updateAccessToken(obj, str)
|
||||
})
|
||||
|
|
@ -251,7 +275,9 @@ function changeState(bool: Boolean) {
|
|||
const obj = {
|
||||
is_active: bool
|
||||
}
|
||||
const str = bool ? t('views.applicationOverview.appInfo.changeState.enableSuccess') : t('views.applicationOverview.appInfo.changeState.disableSuccess')
|
||||
const str = bool
|
||||
? t('views.applicationOverview.appInfo.changeState.enableSuccess')
|
||||
: t('views.applicationOverview.appInfo.changeState.disableSuccess')
|
||||
updateAccessToken(obj, str)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,14 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import LDAP from './component/LDAP.vue'
|
||||
import { t } from '@/locales'
|
||||
import useStore from '@/stores'
|
||||
|
||||
const { user } = useStore()
|
||||
const router = useRouter()
|
||||
|
||||
const activeName = ref('LDAP')
|
||||
const tabList = [
|
||||
{
|
||||
|
|
@ -31,7 +37,11 @@ const tabList = [
|
|||
|
||||
function handleClick() {}
|
||||
|
||||
onMounted(() => {})
|
||||
onMounted(() => {
|
||||
if (user.isExpire()) {
|
||||
router.push({ path: `/application` })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.authentication-setting__main {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import applicationApi from '@/api/application'
|
||||
import useStore from '@/stores'
|
||||
const route = useRoute()
|
||||
const {
|
||||
|
|
@ -46,9 +45,9 @@ function getAccessToken(token: string) {
|
|||
})
|
||||
}
|
||||
function getAppProfile() {
|
||||
applicationApi
|
||||
.getAppProfile(loading)
|
||||
.then((res) => {
|
||||
application
|
||||
.asyncGetAppProfile(loading)
|
||||
.then((res: any) => {
|
||||
applicationDetail.value = res.data
|
||||
})
|
||||
.catch(() => {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,11 @@
|
|||
</div>
|
||||
|
||||
<!-- 历史记录弹出层 -->
|
||||
<div @click.prevent.stop="show = !show" class="chat-popover-button cursor color-secondary">
|
||||
<div
|
||||
v-if="applicationDetail.show_history"
|
||||
@click.prevent.stop="show = !show"
|
||||
class="chat-popover-button cursor color-secondary"
|
||||
>
|
||||
<AppIcon iconName="app-history-outlined"></AppIcon>
|
||||
</div>
|
||||
|
||||
|
|
@ -77,7 +81,6 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive, nextTick, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import applicationApi from '@/api/application'
|
||||
import useStore from '@/stores'
|
||||
const route = useRoute()
|
||||
const {
|
||||
|
|
@ -159,11 +162,13 @@ 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
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
<component :is="currentTemplate" :key="route.fullPath" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, computed } from 'vue'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import useStore from '@/stores'
|
||||
const { user } = useStore()
|
||||
const { application, user } = useStore()
|
||||
|
||||
const components: any = import.meta.glob('@/views/chat/**/index.vue', {
|
||||
eager: true
|
||||
|
|
@ -16,12 +16,32 @@ const {
|
|||
} = route as any
|
||||
|
||||
const currentTemplate = computed(() => {
|
||||
const name = `/src/views/chat/${mode || 'pc'}/index.vue`
|
||||
let modeName = ''
|
||||
if (mode && mode === 'embed') {
|
||||
modeName = 'embed'
|
||||
} else {
|
||||
modeName = show_history.value ? 'pc' : 'base'
|
||||
}
|
||||
|
||||
const name = `/src/views/chat/${modeName}/index.vue`
|
||||
return components[name].default
|
||||
})
|
||||
const loading = ref(false)
|
||||
|
||||
const show_history = ref(false)
|
||||
|
||||
function getAppProfile() {
|
||||
application.asyncGetAppProfile(loading).then((res: any) => {
|
||||
show_history.value = res.data?.show_history
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
user.asyncGetProfile()
|
||||
user.asyncGetProfile().then(() => {
|
||||
if (user.isEnterprise()) {
|
||||
getAppProfile()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted, computed, watch, nextTick } from 'vue'
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
import { useRouter, onBeforeRouteLeave } from 'vue-router'
|
||||
import type { FormInstance, FormRules, UploadFiles } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import LoginPreview from './LoginPreview.vue'
|
||||
|
|
@ -144,6 +144,7 @@ import { MsgSuccess, MsgError } from '@/utils/message'
|
|||
import useStore from '@/stores'
|
||||
|
||||
const { user } = useStore()
|
||||
const router = useRouter()
|
||||
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
user.setTheme(cloneTheme.value)
|
||||
|
|
@ -224,6 +225,9 @@ const updataTheme = async (formEl: FormInstance | undefined, test?: string) => {
|
|||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (user.isExpire()) {
|
||||
router.push({ path: `/application` })
|
||||
}
|
||||
if (themeInfo.value) {
|
||||
themeForm.value = themeInfo.value
|
||||
cloneTheme.value = cloneDeep(themeInfo.value)
|
||||
|
|
|
|||
Loading…
Reference in New Issue