style: Assistant embedded with appearance configuration

This commit is contained in:
fit2cloud-chenyw
2025-08-21 16:52:45 +08:00
parent 5783f3ebf1
commit e6085a4c1b
6 changed files with 153 additions and 105 deletions
+6 -1
View File
@@ -104,6 +104,11 @@ async def ui(session: SessionDep, data: str = Form(), files: List[UploadFile] =
db_model.configuration = json.dumps(config_obj, ensure_ascii=False)
session.add(db_model)
session.commit()
await clear_ui_cache(db_model.id)
@clear_cache(namespace=CacheNamespace.EMBEDDED_INFO, cacheName=CacheName.ASSISTANT_INFO, keyExpression="id")
async def clear_ui_cache(id: int):
pass
@router.get("", response_model=list[AssistantModel])
async def query(session: SessionDep):
@@ -112,7 +117,7 @@ async def query(session: SessionDep):
@router.post("")
async def add(request: Request, session: SessionDep, creator: AssistantBase):
save(request, session, creator)
await save(request, session, creator)
@router.put("")
+98 -82
View File
@@ -8,8 +8,9 @@
header_font_color: 'rgb(100, 106, 115)',
x_type: 'right',
y_type: 'bottom',
x_value: '-5',
y_value: '33',
x_val: '30',
y_val: '30',
float_icon_drag: false,
}
const script_id_prefix = 'sqlbot-assistant-float-script-'
const guideHtml = `
@@ -35,8 +36,8 @@
const chatButtonHtml = (data) => `
<div class="sqlbot-assistant-chat-button">
<img style="height:100%;width:100%;display:none;" src="${data.float_icon}">
<svg style="display:none;margin-right: 20px;" data-v-39a51454="" xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="none">
<img style="height:30px;width:30px;display:none;" src="${data.float_icon}">
<svg style="display:none;" data-v-39a51454="" xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="none">
<path fill="#149CC5" d="M28.333 13.669h-.231c.138.758.207 1.527.206 2.298.005.946-.107 1.89-.335 2.808h.65a1.05 1.05 0 0 0 1.05-1.05V15.01a1.343 1.343 0 0 0-1.34-1.341"></path>
<path fill="#69CAA4" d="M1.692 15.967c0-.77.068-1.54.206-2.298h-.23a1.34 1.34 0 0 0-1.342 1.34v2.716a1.05 1.05 0 0 0 1.05 1.05h.651a11.4 11.4 0 0 1-.335-2.808"></path>
<path fill="url(#a)" d="M15 3.795c-6.89 0-12.474 5.282-12.474 12.171 0 6.89 5.585 10.239 12.474 10.239s12.474-3.35 12.474-10.238c0-6.89-5.585-12.172-12.474-12.172m3.026 17.33h-6.052a6 6 0 0 1-2.484-.535c-.564-.256-2.208.282-2.663-.127-.551-.495.117-1.969-.221-2.636a6.014 6.014 0 0 1 5.368-8.73h6.052a6.014 6.014 0 0 1 0 12.029"></path>
@@ -158,16 +159,16 @@
}
const drag = (e) => {
if (['touchmove', 'touchstart'].includes(e.type)) {
chat_button.style.top = e.touches[0].clientY - chat_button_img.naturalHeight / 2 + 'px'
chat_button.style.left = e.touches[0].clientX - chat_button_img.naturalWidth / 2 + 'px'
chat_button.style.top = e.touches[0].clientY - chat_button_img.clientHeight / 2 + 'px'
chat_button.style.left = e.touches[0].clientX - chat_button_img.clientHeight / 2 + 'px'
} else {
chat_button.style.top = e.y - chat_button_img.naturalHeight / 2 + 'px'
chat_button.style.left = e.x - chat_button_img.naturalWidth / 2 + 'px'
chat_button.style.top = e.y - chat_button_img.clientHeight / 2 + 'px'
chat_button.style.left = e.x - chat_button_img.clientHeight / 2 + 'px'
}
chat_button.style.width = chat_button_img.naturalWidth + 'px'
chat_button.style.height = chat_button_img.naturalHeight + 'px'
chat_button.style.width = chat_button_img.clientHeight + 'px'
chat_button.style.height = chat_button_img.clientHeight + 'px'
}
if (data.is_draggable) {
if (data.float_icon_drag) {
chat_button.addEventListener('drag', drag)
chat_button.addEventListener('dragover', (e) => {
e.preventDefault()
@@ -234,14 +235,14 @@
height: 64px;
box-shadow: 1px 1px 1px 9999px rgba(0,0,0,.6);
position: absolute;
${data.x_type}: ${data.x_value}px;
${data.y_type}: ${data.y_value}px;
${data.x_type}: ${data.x_val}px;
${data.y_type}: ${data.y_val}px;
z-index: 10001;
}
#sqlbot-assistant .sqlbot-assistant-tips {
position: fixed;
${data.x_type}:calc(${data.x_value}px + 75px);
${data.y_type}: calc(${data.y_value}px + 0px);
${data.x_type}:calc(${data.x_val}px + 75px);
${data.y_type}: calc(${data.y_val}px + 0px);
padding: 22px 24px 24px;
border-radius: 6px;
color: #ffffff;
@@ -297,76 +298,76 @@
display:none;
}
@media only screen and (max-width: 768px) {
#sqlbot-assistant-chat-container {
width: 100%;
height: 70%;
right: 0 !important;
}
}
#sqlbot-assistant .sqlbot-assistant-chat-button{
position: fixed;
${data.x_type}: ${data.x_value}px;
${data.y_type}: ${data.y_value}px;
cursor: pointer;
z-index:10000;
#sqlbot-assistant-chat-container {
width: 100%;
height: 70%;
right: 0 !important;
}
#sqlbot-assistant #sqlbot-assistant-chat-container{
z-index:10000;position: relative;
border-radius: 8px;
//border: 1px solid #ffffff;
background: linear-gradient(188deg, rgba(235, 241, 255, 0.20) 39.6%, rgba(231, 249, 255, 0.20) 94.3%), #EFF0F1;
box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.10);
position: fixed;bottom: 16px;right: 16px;overflow: hidden;
}
#sqlbot-assistant .sqlbot-assistant-chat-button{
position: fixed;
${data.x_type}: ${data.x_val}px;
${data.y_type}: ${data.y_val}px;
cursor: pointer;
z-index:10000;
}
#sqlbot-assistant #sqlbot-assistant-chat-container{
z-index:10000;position: relative;
border-radius: 8px;
//border: 1px solid #ffffff;
background: linear-gradient(188deg, rgba(235, 241, 255, 0.20) 39.6%, rgba(231, 249, 255, 0.20) 94.3%), #EFF0F1;
box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.10);
position: fixed;bottom: 16px;right: 16px;overflow: hidden;
}
.ed-overlay-dialog {
margin-top: 50px;
}
.ed-drawer {
margin-top: 50px;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-operate{
top: 18px;
right: 15px;
position: absolute;
display: flex;
align-items: center;
line-height: 18px;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-operate .sqlbot-assistant-chat-close{
margin-left:15px;
cursor: pointer;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-operate .sqlbot-assistant-openviewport{
cursor: pointer;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-operate .sqlbot-assistant-closeviewport{
cursor: pointer;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-viewportnone{
display:none;
}
#sqlbot-assistant #sqlbot-assistant-chat-container #sqlbot-assistant-chat-iframe-${data.id} {
height:100%;
width:100%;
border: none;
}
#sqlbot-assistant #sqlbot-assistant-chat-container {
animation: appear .4s ease-in-out;
}
@keyframes appear {
from {
height: 0;;
}
.ed-overlay-dialog {
margin-top: 50px;
}
.ed-drawer {
margin-top: 50px;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-operate{
top: 18px;
right: 15px;
position: absolute;
display: flex;
align-items: center;
line-height: 18px;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-operate .sqlbot-assistant-chat-close{
margin-left:15px;
cursor: pointer;
to {
height: 600px;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-operate .sqlbot-assistant-openviewport{
cursor: pointer;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-operate .sqlbot-assistant-closeviewport{
cursor: pointer;
}
#sqlbot-assistant #sqlbot-assistant-chat-container .sqlbot-assistant-viewportnone{
display:none;
}
#sqlbot-assistant #sqlbot-assistant-chat-container #sqlbot-assistant-chat-iframe-${data.id}{
height:100%;
width:100%;
border: none;
}
#sqlbot-assistant #sqlbot-assistant-chat-container {
animation: appear .4s ease-in-out;
}
@keyframes appear {
from {
height: 0;;
}
to {
height: 600px;
}
}`.replaceAll('#sqlbot-assistant ', `#${sqlbot_assistantId} `)
}`.replaceAll('#sqlbot-assistant ', `#${sqlbot_assistantId} `)
root.appendChild(style)
}
function getParam(src, key) {
@@ -460,7 +461,10 @@
}
const data = res.data
const config_json = data.configuration
let tempData = Object.assign(defaultData, { id, domain_url, name: data.name })
let tempData = Object.assign(defaultData, data)
if (tempData.configuration) {
delete tempData.configuration
}
if (config_json) {
const config = JSON.parse(config_json)
if (config) {
@@ -468,6 +472,18 @@
tempData = Object.assign(tempData, config)
}
}
tempData['id'] = id
tempData['domain_url'] = domain_url
if (tempData['float_icon'] && !tempData['float_icon'].startsWith('http://')) {
tempData['float_icon'] =
`${domain_url}/api/v1/system/assistant/picture/${tempData['float_icon']}`
if (domain_url.includes('5173')) {
tempData['float_icon'] = tempData['float_icon'].replace('5173', '8000')
}
}
tempData['online'] = online && online.toString().toLowerCase() == 'true'
initsqlbot_assistant(tempData)
if (data.type == 1) {
@@ -0,0 +1,6 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="" xmlns="http://www.w3.org/2000/svg">
<path d="M15.75 14.25V15.75H2.25V14.25H15.75ZM15.75 3.75H2.25V15.75L2.09692 15.7419C1.39076 15.6704 0.82965 15.1092 0.758057 14.4031L0.75 14.25V3.75C0.75 2.92157 1.42157 2.25 2.25 2.25H15.75C16.5784 2.25 17.25 2.92157 17.25 3.75V14.25C17.25 15.0784 16.5784 15.75 15.75 15.75V3.75Z" fill=""/>
<path d="M5.625 5.25H3.375C3.27554 5.25 3.18016 5.28951 3.10983 5.35984C3.03951 5.43016 3 5.52554 3 5.625V6.375C3 6.47446 3.03951 6.56984 3.10983 6.64017C3.18016 6.71049 3.27554 6.75 3.375 6.75H5.625C5.67425 6.75 5.72301 6.7403 5.76851 6.72145C5.814 6.70261 5.85534 6.67499 5.89017 6.64017C5.92499 6.60534 5.95261 6.564 5.97146 6.51851C5.9903 6.47301 6 6.42425 6 6.375V5.625C6 5.57575 5.9903 5.52699 5.97146 5.48149C5.95261 5.436 5.92499 5.39466 5.89017 5.35984C5.85534 5.32501 5.814 5.29739 5.76851 5.27855C5.72301 5.2597 5.67425 5.25 5.625 5.25Z" fill=""/>
<path d="M5.625 7.5H3.375C3.27554 7.5 3.18016 7.53951 3.10983 7.60984C3.03951 7.68016 3 7.77554 3 7.875V8.625C3 8.72446 3.03951 8.81984 3.10983 8.89017C3.18016 8.96049 3.27554 9 3.375 9H5.625C5.67425 9 5.72301 8.9903 5.76851 8.97145C5.814 8.95261 5.85534 8.92499 5.89017 8.89017C5.92499 8.85534 5.95261 8.814 5.97146 8.76851C5.9903 8.72301 6 8.67425 6 8.625V7.875C6 7.82575 5.9903 7.77699 5.97146 7.73149C5.95261 7.686 5.92499 7.64466 5.89017 7.60984C5.85534 7.57501 5.814 7.54739 5.76851 7.52855C5.72301 7.5097 5.67425 7.5 5.625 7.5Z" fill=""/>
<path d="M6.75 3.75H8.25V14.25H6.75V3.75Z" fill=""/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,6 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="" xmlns="http://www.w3.org/2000/svg">
<path d="M15.75 14.25V15.75H2.25V14.25H15.75ZM15.75 3.75H2.25V15.75L2.09692 15.7419C1.39076 15.6704 0.82965 15.1092 0.758057 14.4031L0.75 14.25V3.75C0.75 2.92157 1.42157 2.25 2.25 2.25H15.75C16.5784 2.25 17.25 2.92157 17.25 3.75V14.25C17.25 15.0784 16.5784 15.75 15.75 15.75V3.75Z" fill=""/>
<path d="M5.625 5.25H3.375C3.27554 5.25 3.18016 5.28951 3.10983 5.35984C3.03951 5.43016 3 5.52554 3 5.625V6.375C3 6.47446 3.03951 6.56984 3.10983 6.64017C3.18016 6.71049 3.27554 6.75 3.375 6.75H5.625C5.67425 6.75 5.72301 6.7403 5.76851 6.72145C5.814 6.70261 5.85534 6.67499 5.89017 6.64017C5.92499 6.60534 5.95261 6.564 5.97146 6.51851C5.9903 6.47301 6 6.42425 6 6.375V5.625C6 5.57575 5.9903 5.52699 5.97146 5.48149C5.95261 5.436 5.92499 5.39466 5.89017 5.35984C5.85534 5.32501 5.814 5.29739 5.76851 5.27855C5.72301 5.2597 5.67425 5.25 5.625 5.25Z" fill=""/>
<path d="M5.625 7.5H3.375C3.27554 7.5 3.18016 7.53951 3.10983 7.60984C3.03951 7.68016 3 7.77554 3 7.875V8.625C3 8.72446 3.03951 8.81984 3.10983 8.89017C3.18016 8.96049 3.27554 9 3.375 9H5.625C5.67425 9 5.72301 8.9903 5.76851 8.97145C5.814 8.95261 5.85534 8.92499 5.89017 8.89017C5.92499 8.85534 5.95261 8.814 5.97146 8.76851C5.9903 8.72301 6 8.67425 6 8.625V7.875C6 7.82575 5.9903 7.77699 5.97146 7.73149C5.95261 7.686 5.92499 7.64466 5.89017 7.60984C5.85534 7.57501 5.814 7.54739 5.76851 7.52855C5.72301 7.5097 5.67425 7.5 5.625 7.5Z" fill=""/>
<path d="M6.75 3.75H8.25V14.25H6.75V3.75Z" fill=""/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+11 -1
View File
@@ -105,7 +105,17 @@
</template>
<div v-else class="assistant-desc">
<img :src="logoAssistant" class="logo" width="30px" height="30px" alt="" />
<img
v-if="logoAssistant"
:src="logoAssistant"
class="logo"
width="30px"
height="30px"
alt=""
/>
<el-icon v-else size="32">
<logo_fold />
</el-icon>
<div class="i-am">{{ welcome }}</div>
<div class="i-can">{{ welcomeDesc }}</div>
</div>
+26 -21
View File
@@ -1,6 +1,6 @@
<template>
<div class="sqlbot-assistant-container">
<div class="header">
<div class="header" :style="{ color: customSet.header_font_color }">
<el-icon size="20" @click="openHistory">
<icon_sidebar_outlined></icon_sidebar_outlined>
</el-icon>
@@ -30,13 +30,14 @@ import { onBeforeMount, nextTick, onBeforeUnmount, ref, onMounted, reactive } fr
import ChatComponent from '@/views/chat/index.vue'
import { request } from '@/utils/request'
import LOGO from '@/assets/embedded/LOGO.png'
import icon_sidebar_outlined from '@/assets/embedded/icon_sidebar_outlined.svg'
import icon_sidebar_outlined from '@/assets/embedded/icon_sidebar_outlined_nofill.svg'
import icon_new_chat_outlined from '@/assets/svg/icon_new_chat_outlined.svg'
import { useRoute } from 'vue-router'
import { assistantApi } from '@/api/assistant'
import { useAssistantStore } from '@/stores/assistant'
import { setCurrentColor } from '@/utils/utils'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const assistantStore = useAssistantStore()
const route = useRoute()
const chatRef = ref()
@@ -101,8 +102,8 @@ onMounted(() => {
})
const customSet = reactive({
name: '',
welcome: '',
welcome_desc: '',
welcome: t('embedded.i_am_sqlbot'),
welcome_desc: t('embedded.data_analysis_now'),
theme: '#1CBA90',
header_font_color: '#1F2329',
}) as { [key: string]: any }
@@ -124,23 +125,7 @@ onBeforeMount(async () => {
ElMessage.error('Miss assistant id, please check assistant url')
return
}
request.get(`/system/assistant/info/${assistantId}`).then((res) => {
if (res?.configuration) {
const rawData = JSON.parse(res?.configuration)
logo.value = baseUrl + rawData.logo
for (const key in customSet) {
if (Object.prototype.hasOwnProperty.call(customSet, key) && rawData[key]) {
customSet[key] = rawData[key]
}
}
nextTick(() => {
setPageCustomColor(customSet.theme)
setPageHeaderFontColor(customSet.header_font_color)
})
}
})
const online = route.query.online
setFormatOnline(online)
@@ -165,6 +150,26 @@ onBeforeMount(async () => {
messageId: assistantId,
}
window.parent.postMessage(readyData, '*')
request.get(`/system/assistant/${assistantId}`).then((res) => {
if (res?.configuration) {
const rawData = JSON.parse(res?.configuration)
if (rawData.logo) {
logo.value = baseUrl + rawData.logo
}
for (const key in customSet) {
if (Object.prototype.hasOwnProperty.call(customSet, key) && rawData[key]) {
customSet[key] = rawData[key]
}
}
nextTick(() => {
setPageCustomColor(customSet.theme)
setPageHeaderFontColor(customSet.header_font_color)
})
}
})
})
onBeforeUnmount(() => {