This commit is contained in:
2025-09-08 16:38:14 +08:00
parent 9e003dc99f
commit 9b55e816ba

View File

@@ -0,0 +1,544 @@
<script lang="ts" setup>
import Assistant from '@/views/embedded/AssistantPreview.vue'
import { ref, unref, reactive, nextTick } from 'vue'
import { type UploadUserFile, ElMessage } from 'element-plus-secondary'
import { useI18n } from 'vue-i18n'
import { request } from '@/utils/request'
import { setCurrentColor } from '@/utils/utils'
import { cloneDeep } from 'lodash-es'
import { useAppearanceStoreWithOut } from '@/stores/appearance'
const { t } = useI18n()
const appearanceStore = useAppearanceStoreWithOut()
const currentId = ref()
interface SqlBotForm {
theme: string
header_font_color: string
logo?: string
x_type: string
y_type: string
welcome_desc: string
float_icon?: string
welcome: string
float_icon_drag: boolean
x_val: number
y_val: number
}
const optionsY = [
{
label: t('embedded.up'),
value: 'top',
},
{
label: t('embedded.down'),
value: 'bottom',
},
]
const optionsX = [
{
label: t('embedded.left'),
value: 'left',
},
{
label: t('embedded.right'),
value: 'right',
},
]
const COLOR_PANEL = [
'#FF4500',
'#FF8C00',
'#FFD700',
'#71AE46',
'#00CED1',
'#1E90FF',
'#C71585',
'#999999',
'#000000',
'#FFFFFF',
]
const fileList = ref<(UploadUserFile & { flag: string })[]>([])
const dialogVisible = ref(false)
const logo = ref('')
const floatIcon = ref('')
const defaultSqlBotForm = reactive<SqlBotForm>({
x_type: 'right',
y_type: 'bottom',
x_val: 30,
y_val: 30,
float_icon_drag: false,
welcome: t('embedded.i_am_sqlbot'),
welcome_desc: t('embedded.data_analysis_now'),
theme: '#1CBA90',
header_font_color: '#1F2329',
logo: '',
float_icon: '',
})
const sqlBotForm = reactive<SqlBotForm>(cloneDeep(defaultSqlBotForm)) as { [key: string]: any }
let rawData = {} as { [key: string]: any }
const init = () => {
Object.assign(sqlBotForm, cloneDeep(defaultSqlBotForm))
fileList.value = []
logo.value = rawData.logo
floatIcon.value = rawData.float_icon
for (const key in sqlBotForm) {
if (
Object.prototype.hasOwnProperty.call(sqlBotForm, key) &&
![null, undefined].includes(rawData[key])
) {
sqlBotForm[key] = rawData[key]
}
}
if (!rawData.theme) {
const { customColor, themeColor } = appearanceStore
const currentColor =
themeColor === 'custom' && customColor
? customColor
: themeColor === 'blue'
? '#3370ff'
: '#1CBA90'
sqlBotForm.theme = currentColor || sqlBotForm.theme
}
nextTick(() => {
setPageCustomColor(sqlBotForm.theme)
setPageHeaderFontColor(sqlBotForm.header_font_color)
})
}
const giveUp = () => {
resetSqlBotForm(false)
init()
dialogVisible.value = false
}
const emits = defineEmits(['refresh'])
const saveHandler = () => {
const param = buildParam()
const url = '/system/assistant/ui'
request
.patch(url, param, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then((res) => {
if (!res) {
ElMessage.success(t('system.setting_successfully'))
dialogVisible.value = false
emits('refresh')
}
})
}
const buildParam = () => {
const formData = new FormData()
if (fileList.value.length) {
fileList.value.forEach((file: any) => {
const name = file.name + ',' + file['flag']
const fileArray = [file]
const newfile = new File(fileArray, name, { type: file['type'] })
formData.append('files', newfile)
})
}
formData.append('data', JSON.stringify({ ...unref(sqlBotForm), id: currentId.value }))
return formData
}
const headerFontColorChange = (val: any) => {
setPageHeaderFontColor(val)
}
const customColorChange = (val: any) => {
setPageCustomColor(val)
}
const setPageCustomColor = (val: any) => {
const ele = document.querySelector('.ui-main') as HTMLElement
setCurrentColor(val, ele)
}
const setPageHeaderFontColor = (val: any) => {
const ele = document.getElementsByClassName('ui-main')[0] as HTMLElement
ele.style.setProperty('--ed-text-color-primary', val)
}
const resetSqlBotForm = (reset2Default?: boolean) => {
Object.assign(sqlBotForm, cloneDeep(defaultSqlBotForm))
clearFiles(['logo', 'float_icon'])
if (reset2Default) {
logo.value = ''
floatIcon.value = ''
sqlBotForm.restoreDefaults = true
nextTick(() => {
setPageCustomColor(sqlBotForm.theme)
setPageHeaderFontColor(sqlBotForm.header_font_color)
})
}
}
const uploadImg = (options: any) => {
const file = options.file
if (file['flag'] === 'logo') {
logo.value = URL.createObjectURL(file)
} else if (file['flag'] === 'float_icon') {
floatIcon.value = URL.createObjectURL(file)
}
}
const beforeUpload = (file: any, type: any) => {
let len = fileList.value?.length
let match = false
file.flag = type
while (len--) {
const tfile = fileList.value[len]
if (type == tfile['flag']) {
fileList.value[len] = file
match = true
}
}
if (!match) {
fileList.value?.push(file)
}
return true
}
const clearFiles = (array?: string[]) => {
if (!array?.length || !fileList.value?.length) {
fileList.value = []
return
}
let len = fileList.value.length
while (len--) {
const file = fileList.value[len]
if (array.includes(file['flag'])) {
fileList.value.splice(len, 1)
}
}
}
const appName = ref('')
const open = (row: any) => {
rawData = JSON.parse(row.configuration)
currentId.value = row.id
appName.value = row.name
dialogVisible.value = true
init()
}
defineExpose({
open,
})
</script>
<template>
<el-dialog
v-model="dialogVisible"
:title="$t('embedded.display_settings')"
width="1000"
modal-class="embed-third_party_ui"
>
<div class="ui-main">
<div class="left-preview">
<assistant
:welcome-desc="sqlBotForm.welcome_desc"
:welcome="sqlBotForm.welcome"
:name="appName"
:logo="logo"
></assistant>
</div>
<div class="right-form">
<div style="display: flex; align-items: center; justify-content: space-between">
<div class="theme">
<div class="theme-bg">{{ $t('system.customize_theme_color') }}</div>
<el-color-picker
v-model="sqlBotForm.theme"
:trigger-width="28"
:predefine="COLOR_PANEL"
is-custom
effect="light"
@change="customColorChange"
/>
</div>
<div class="theme">
<div class="theme-bg">{{ $t('embedded.header_text_color') }}</div>
<el-color-picker
v-model="sqlBotForm.header_font_color"
:trigger-width="28"
:predefine="COLOR_PANEL"
is-custom
effect="light"
@change="headerFontColorChange"
/>
</div>
</div>
<div class="config-item" style="margin-top: 3px">
<div class="config-logo">
<span class="logo">{{ $t('embedded.app_logo') }}</span>
<el-upload
name="logo"
:show-file-list="false"
accept=".jpg,.png,.gif,.svg"
:before-upload="(e: any) => beforeUpload(e, 'logo')"
:http-request="uploadImg"
>
<el-button secondary>{{ t('embedded.replace') }}</el-button>
</el-upload>
</div>
<div class="tips">{{ $t('embedded.maximum_size_10mb') }}</div>
</div>
<div class="config-item float-icon">
<div class="config-logo">
<span class="logo">{{ $t('embedded.window_entrance_icon') }}</span>
<el-upload
name="float_icon"
:show-file-list="false"
accept=".jpg,.png,.gif,.svg"
:before-upload="(e: any) => beforeUpload(e, 'float_icon')"
:http-request="uploadImg"
>
<el-button secondary>{{ t('embedded.replace') }}</el-button>
</el-upload>
</div>
<div class="tips">{{ $t('embedded.maximum_size_10mb') }}</div>
<div style="background: #1f232926; height: 1px; margin: 12px 0"></div>
<div class="position-set">
{{ $t('embedded.default_icon_position') }}
<el-checkbox
v-model="sqlBotForm.float_icon_drag"
:label="$t('embedded.draggable_position')"
/>
</div>
<div class="position-set_input">
<div class="x">
<el-input-number
v-model="sqlBotForm.x_val"
step-strictly
:min="0"
controls-position="right"
>
<template #prefix>
<el-select v-model="sqlBotForm.x_type" style="width: 51px">
<el-option
v-for="item in optionsX"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template> </el-input-number
>px
</div>
<div class="y">
<el-input-number
v-model="sqlBotForm.y_val"
step-strictly
:min="0"
controls-position="right"
>
<template #prefix>
<el-select v-model="sqlBotForm.y_type" style="width: 51px">
<el-option
v-for="item in optionsY"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template> </el-input-number
>px
</div>
</div>
</div>
<el-form
label-position="top"
require-asterisk-position="right"
label-width="120px"
class="page-Form"
>
<el-form-item :label="$t('system.welcome_message')">
<el-input
v-model="sqlBotForm.welcome"
:placeholder="
$t('datasource.please_enter') + $t('common.empty') + $t('system.welcome_message')
"
maxlength="50"
/>
</el-form-item>
<el-form-item :label="$t('embedded.welcome_description')">
<el-input
v-model="sqlBotForm.welcome_desc"
:placeholder="
$t('datasource.please_enter') +
$t('common.empty') +
$t('embedded.welcome_description')
"
type="textarea"
show-word-limit
maxlength="50"
/>
</el-form-item>
</el-form>
<div class="btns">
<el-button secondary @click="resetSqlBotForm(true)">{{
t('system.restore_default')
}}</el-button>
<el-button style="margin-left: auto" secondary @click="giveUp">{{
t('common.cancel')
}}</el-button>
<el-button type="primary" @click="saveHandler">{{ t('common.save') }}</el-button>
</div>
</div>
</div>
</el-dialog>
</template>
<style lang="less">
.embed-third_party_ui {
.ui-main {
display: flex;
align-items: center;
justify-content: space-between;
height: 628px;
.left-preview {
width: 460px;
height: 100%;
.content {
pointer-events: none;
}
}
.right-form {
width: 470px;
height: 100%;
.theme {
width: 223px;
.ed-color-picker__trigger {
padding: 0 !important;
height: 26px !important;
width: 26px !important;
}
.ed-color-picker__icon {
display: none;
}
.ed-color-picker {
height: 28px !important;
padding: 0;
}
.theme-bg {
font-size: 14px;
font-weight: 400;
line-height: 22px;
margin: 0 0 8px 0;
}
.ed-color-picker {
height: 32px;
.ed-color-picker__trigger {
height: 100%;
padding: 8px;
}
}
}
.config-item {
min-height: 86px;
margin-bottom: 8px;
padding: 16px;
border-radius: 6px;
border: 1px solid #dee0e3;
background: #fff;
.config-logo {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
.logo {
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
.ed-button {
min-width: 48px;
height: 28px;
line-height: 28px;
padding: 4px 7px;
font-size: 12px;
font-weight: 400;
}
}
.tips {
font-size: 12px;
font-weight: 400;
line-height: 18px;
white-space: pre-wrap;
color: #8f959e;
}
}
.position-set,
.position-set_input {
display: flex;
align-items: center;
justify-content: space-between;
}
.position-set_input {
margin-top: 8px;
.x,
.y {
display: flex;
align-items: center;
.ed-input-number.is-controls-right .ed-input-number__increase {
--ed-input-number-controls-height: 17px;
}
.ed-input-number {
margin-right: 8px;
}
.ed-input-number.is-controls-right .ed-input__wrapper {
padding-left: 1px;
}
.ed-select__wrapper {
box-shadow: none;
padding-right: 0;
}
.ed-input__prefix {
border-right: 1px solid #d9dcdf;
}
}
}
.page-Form {
margin-top: 8px;
.ed-form-item {
margin-bottom: 8px;
}
.ed-form-item--label-top .ed-form-item__label {
height: 22px;
line-height: 22px;
}
.ed-textarea__inner {
padding-bottom: 14px;
}
}
.btns {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 88px;
}
}
}
}
</style>