This commit is contained in:
dataeaseShu
2025-07-29 14:00:26 +08:00
14 changed files with 420 additions and 113 deletions
+1 -1
View File
@@ -147,7 +147,7 @@ class AiModelQuestion(BaseModel):
old_questions=old_questions)
def filter_sys_question(self):
return get_permissions_template()['system'].format(lang=self.lang)
return get_permissions_template()['system'].format(lang=self.lang, engine=self.engine)
def filter_user_question(self):
return get_permissions_template()['user'].format(sql=self.sql, filter=self.filter)
+53 -47
View File
@@ -386,51 +386,56 @@ class LLMService:
]
""" _ds_list = self.session.exec(select(CoreDatasource).options(
load_only(CoreDatasource.id, CoreDatasource.name, CoreDatasource.description))).all() """
_ds_list_dict = []
for _ds in _ds_list:
_ds_list_dict.append(_ds)
datasource_msg.append(
HumanMessage(self.chat_question.datasource_user_question(orjson.dumps(_ds_list_dict).decode())))
ignore_auto_select = _ds_list and len(_ds_list) == 1
# ignore auto select ds
if not ignore_auto_select:
_ds_list_dict = []
for _ds in _ds_list:
_ds_list_dict.append(_ds)
datasource_msg.append(
HumanMessage(self.chat_question.datasource_user_question(orjson.dumps(_ds_list_dict).decode())))
history_msg = []
if self.record.full_select_datasource_message and self.record.full_select_datasource_message.strip() != '':
history_msg = orjson.loads(self.record.full_select_datasource_message)
history_msg = []
if self.record.full_select_datasource_message and self.record.full_select_datasource_message.strip() != '':
history_msg = orjson.loads(self.record.full_select_datasource_message)
self.record = save_full_select_datasource_message_and_answer(session=self.session, record_id=self.record.id,
answer='',
full_message=orjson.dumps(history_msg +
[{'type': msg.type,
'content': msg.content}
for msg
in
datasource_msg]).decode())
full_thinking_text = ''
full_text = ''
token_usage = {}
res = self.llm.stream(datasource_msg)
for chunk in res:
SQLBotLogUtil.info(chunk)
reasoning_content_chunk = ''
if 'reasoning_content' in chunk.additional_kwargs:
reasoning_content_chunk = chunk.additional_kwargs.get('reasoning_content', '')
# else:
# reasoning_content_chunk = chunk.get('reasoning_content')
if reasoning_content_chunk is None:
self.record = save_full_select_datasource_message_and_answer(session=self.session, record_id=self.record.id,
answer='',
full_message=orjson.dumps(history_msg +
[{'type': msg.type,
'content': msg.content}
for msg
in
datasource_msg]).decode())
full_thinking_text = ''
full_text = ''
token_usage = {}
res = self.llm.stream(datasource_msg)
for chunk in res:
SQLBotLogUtil.info(chunk)
reasoning_content_chunk = ''
full_thinking_text += reasoning_content_chunk
if 'reasoning_content' in chunk.additional_kwargs:
reasoning_content_chunk = chunk.additional_kwargs.get('reasoning_content', '')
# else:
# reasoning_content_chunk = chunk.get('reasoning_content')
if reasoning_content_chunk is None:
reasoning_content_chunk = ''
full_thinking_text += reasoning_content_chunk
full_text += chunk.content
yield {'content': chunk.content, 'reasoning_content': reasoning_content_chunk}
get_token_usage(chunk, token_usage)
datasource_msg.append(AIMessage(full_text))
full_text += chunk.content
yield {'content': chunk.content, 'reasoning_content': reasoning_content_chunk}
get_token_usage(chunk, token_usage)
datasource_msg.append(AIMessage(full_text))
json_str = extract_nested_json(full_text)
json_str = extract_nested_json(full_text)
_error: Exception | None = None
_datasource: int | None = None
_engine_type: str | None = None
try:
data: dict = orjson.loads(json_str)
data: dict = _ds_list[0] if ignore_auto_select else orjson.loads(json_str)
if data.get('id') and data.get('id') != 0:
_datasource = data['id']
@@ -465,17 +470,18 @@ class LLMService:
except Exception as e:
_error = e
self.record = save_full_select_datasource_message_and_answer(session=self.session, record_id=self.record.id,
answer=orjson.dumps({'content': full_text,
'reasoning_content': full_thinking_text}).decode(),
datasource=_datasource,
engine_type=_engine_type,
full_message=orjson.dumps(history_msg +
[{'type': msg.type,
'content': msg.content}
for msg
in
datasource_msg]).decode())
if not ignore_auto_select:
self.record = save_full_select_datasource_message_and_answer(session=self.session, record_id=self.record.id,
answer=orjson.dumps({'content': full_text,
'reasoning_content': full_thinking_text}).decode(),
datasource=_datasource,
engine_type=_engine_type,
full_message=orjson.dumps(history_msg +
[{'type': msg.type,
'content': msg.content}
for msg
in
datasource_msg]).decode())
self.init_messages()
if _error:
@@ -815,7 +821,7 @@ class LLMService:
SQLBotLogUtil.info(full_sql_text)
# todo row permission
if is_normal_user(self.current_user) or (self.current_assistant and self.current_assistant.type == 1):
if (not self.current_assistant and is_normal_user(self.current_user)) or (self.current_assistant and self.current_assistant.type == 1):
sql_json_str = extract_nested_json(full_sql_text)
data = orjson.loads(sql_json_str)
+1 -1
View File
@@ -41,7 +41,7 @@ def get_ds(session: SessionDep, id: int):
def check_status(session: SessionDep, ds: CoreDatasource, is_raise: bool = False):
conn = get_engine(ds)
conn = get_engine(ds, 10)
try:
with conn.connect() as connection:
SQLBotLogUtil.info("success")
+8 -12
View File
@@ -52,28 +52,24 @@ def get_uri_from_config(type: str, conf: DatasourceConf) -> str:
return db_url
def get_engine(ds: CoreDatasource) -> Engine:
def get_engine(ds: CoreDatasource, timeout: int = 0) -> Engine:
conf = DatasourceConf(**json.loads(aes_decrypt(ds.configuration))) if ds.type != "excel" else get_engine_config()
if conf.timeout is None:
conf.timeout = 30
conf.timeout = timeout
if timeout > 0:
conf.timeout = timeout
if ds.type == "pg" and (conf.dbSchema is not None and conf.dbSchema != ""):
engine = create_engine(get_uri(ds),
connect_args={"options": f"-c search_path={urllib.parse.quote(conf.dbSchema)}",
"connect_timeout": conf.timeout},
pool_timeout=conf.timeout, pool_size=20, max_overflow=10)
pool_timeout=conf.timeout)
elif ds.type == 'sqlServer':
engine = create_engine(get_uri(ds), pool_timeout=conf.timeout,
pool_size=20,
max_overflow=10)
engine = create_engine(get_uri(ds), pool_timeout=conf.timeout)
elif ds.type == 'oracle':
engine = create_engine(get_uri(ds),
pool_timeout=conf.timeout,
pool_size=20,
max_overflow=10)
pool_timeout=conf.timeout)
else:
engine = create_engine(get_uri(ds), connect_args={"connect_timeout": conf.timeout}, pool_timeout=conf.timeout,
pool_size=20,
max_overflow=10)
engine = create_engine(get_uri(ds), connect_args={"connect_timeout": conf.timeout}, pool_timeout=conf.timeout)
return engine
+1 -3
View File
@@ -25,9 +25,7 @@ def get_engine_conn():
db_url = get_engine_uri(conf)
engine = create_engine(db_url,
connect_args={"options": f"-c search_path={conf.dbSchema}", "connect_timeout": conf.timeout},
pool_timeout=conf.timeout,
pool_size=20,
max_overflow=10)
pool_timeout=conf.timeout)
return engine
+54 -32
View File
@@ -1,29 +1,33 @@
import json
import urllib
from typing import Optional
from fastapi import FastAPI
import requests
from fastapi import FastAPI
from sqlalchemy import Engine, create_engine
from sqlmodel import Session, select
from apps.datasource.models.datasource import CoreDatasource, DatasourceConf
from starlette.middleware.cors import CORSMiddleware
from apps.datasource.models.datasource import CoreDatasource, DatasourceConf
from apps.system.models.system_model import AssistantModel
from apps.system.schemas.auth import CacheName, CacheNamespace
from apps.system.schemas.system_schema import AssistantHeader, AssistantOutDsSchema, UserInfoDTO
from common.core.sqlbot_cache import cache
from common.core.db import engine
from starlette.middleware.cors import CORSMiddleware
from common.core.config import settings
from common.core.db import engine
from common.core.sqlbot_cache import cache
from common.utils.utils import string_to_numeric_hash
@cache(namespace=CacheNamespace.EMBEDDED_INFO, cacheName=CacheName.ASSISTANT_INFO, keyExpression="assistant_id")
async def get_assistant_info(*, session: Session, assistant_id: int) -> AssistantModel | None:
db_model = session.get(AssistantModel, assistant_id)
return db_model
def get_assistant_user(*, id: int):
return UserInfoDTO(id=id, account="sqlbot-inner-assistant", oid=1, name="sqlbot-inner-assistant", email="[email protected]")
return UserInfoDTO(id=id, account="sqlbot-inner-assistant", oid=1, name="sqlbot-inner-assistant",
email="[email protected]")
def get_assistant_ds(llm_service) -> list[dict]:
assistant: AssistantHeader = llm_service.current_assistant
@@ -34,13 +38,14 @@ def get_assistant_ds(llm_service) -> list[dict]:
if configuration:
config: dict[any] = json.loads(configuration)
oid: int = int(config['oid'])
stmt = select(CoreDatasource.id, CoreDatasource.name, CoreDatasource.description).where(CoreDatasource.oid == oid)
stmt = select(CoreDatasource.id, CoreDatasource.name, CoreDatasource.description).where(
CoreDatasource.oid == oid)
if not assistant.online:
private_list:list[int] = config.get('private_list') or None
private_list: list[int] = config.get('private_list') or None
if private_list:
stmt = stmt.where(~CoreDatasource.id.in_(private_list))
db_ds_list = session.exec(stmt)
result_list = [
{
"id": ds.id,
@@ -49,7 +54,7 @@ def get_assistant_ds(llm_service) -> list[dict]:
}
for ds in db_ds_list
]
# filter private ds if offline
return result_list
out_ds_instance: AssistantOutDs = AssistantOutDsFactory.get_instance(assistant)
@@ -58,8 +63,9 @@ def get_assistant_ds(llm_service) -> list[dict]:
# format?
return dslist
def init_dynamic_cors(app: FastAPI):
try:
try:
with Session(engine) as session:
list_result = session.exec(select(AssistantModel).order_by(AssistantModel.create_time)).all()
seen = set()
@@ -81,33 +87,35 @@ def init_dynamic_cors(app: FastAPI):
cors_middleware.kwargs['allow_origins'] = updated_origins
except Exception as e:
return False, e
class AssistantOutDs:
assistant: AssistantHeader
ds_list: Optional[list[AssistantOutDsSchema]] = None
certificate: Optional[str] = None
def __init__(self, assistant: AssistantHeader):
self.assistant = assistant
self.ds_list = None
self.certificate = assistant.certificate
self.get_ds_from_api()
#@cache(namespace=CacheNamespace.EMBEDDED_INFO, cacheName=CacheName.ASSISTANT_DS, keyExpression="current_user.id")
# @cache(namespace=CacheNamespace.EMBEDDED_INFO, cacheName=CacheName.ASSISTANT_DS, keyExpression="current_user.id")
def get_ds_from_api(self):
config: dict[any] = json.loads(self.assistant.configuration)
endpoint: str = config['endpoint']
certificateList: list[any] = json.loads(self.certificate)
header = {}
cookies = {}
param = {}
for item in certificateList:
if item['target'] == 'header':
header[item['key']] = item['value']
if item['target'] == 'cookie':
cookies[item['key']] = item['value']
res = requests.get(url=endpoint, headers=header, cookies=cookies, timeout=10)
if item['target'] == 'param':
param[item['key']] = item['value']
res = requests.get(url=endpoint, params=param, headers=header, cookies=cookies, timeout=10)
if res.status_code == 200:
result_json: dict[any] = json.loads(res.text)
if result_json.get('code') == 0:
@@ -116,23 +124,23 @@ class AssistantOutDs:
self.convert2schema(item)
for item in temp_list
]
return self.ds_list
else:
raise Exception(f"Failed to get datasource list from {endpoint}, error: {result_json.get('message')}")
else:
raise Exception(f"Failed to get datasource list from {endpoint}, status code: {res.status_code}")
def get_simple_ds_list(self):
if self.ds_list:
return [{'id': ds.id, 'name': ds.name, 'description': ds.comment} for ds in self.ds_list]
else:
raise Exception("Datasource list is not found.")
def get_db_schema(self, ds_id: int) -> str:
ds = self.get_ds(ds_id)
schema_str = ""
#db_name = ds.db_schema
# db_name = ds.db_schema
db_name = ds.db_schema if ds.db_schema is not None and ds.db_schema != "" else ds.dataBase
schema_str += f"【DB_ID】 {db_name}\n【Schema】\n"
for table in ds.tables:
@@ -142,7 +150,7 @@ class AssistantOutDs:
schema_str += '\n[\n'
else:
schema_str += f", {table_comment}\n[\n"
field_list = []
for field in table.fields:
field_comment = field.comment
@@ -153,7 +161,7 @@ class AssistantOutDs:
schema_str += ",\n".join(field_list)
schema_str += '\n]\n'
return schema_str
def get_ds(self, ds_id: int):
if self.ds_list:
for ds in self.ds_list:
@@ -173,20 +181,22 @@ class AssistantOutDs:
db_schema = ds_dict.get('schema', ds_dict.get('db_schema', ''))
ds_dict.pop("schema", None)
return AssistantOutDsSchema(**{**ds_dict, "id": id, "db_schema": db_schema})
class AssistantOutDsFactory:
@staticmethod
def get_instance(assistant: AssistantHeader) -> AssistantOutDs:
return AssistantOutDs(assistant)
def get_ds_engine(ds: AssistantOutDsSchema) -> Engine:
timeout: int = 30
connect_args = {"connect_timeout": timeout}
conf = DatasourceConf(
host=ds.host,
port=ds.port,
host=ds.host,
port=ds.port,
username=ds.user,
password=ds.password,
password=ds.password,
database=ds.dataBase,
driver='',
extraJdbc=ds.extraParams,
@@ -195,8 +205,20 @@ def get_ds_engine(ds: AssistantOutDsSchema) -> Engine:
conf.extraJdbc = ''
from apps.db.db import get_uri_from_config
uri = get_uri_from_config(ds.type, conf)
# if ds.type == "pg" and ds.db_schema:
# connect_args.update({"options": f"-c search_path={ds.db_schema}"})
# engine = create_engine(uri, connect_args=connect_args, pool_timeout=timeout, pool_size=20, max_overflow=10)
if ds.type == "pg" and ds.db_schema:
connect_args.update({"options": f"-c search_path={ds.db_schema}"})
engine = create_engine(uri, connect_args=connect_args, pool_timeout=timeout, pool_size=20, max_overflow=10)
engine = create_engine(uri,
connect_args={"options": f"-c search_path={urllib.parse.quote(ds.db_schema)}",
"connect_timeout": timeout},
pool_timeout=timeout)
elif ds.type == 'sqlServer':
engine = create_engine(uri, pool_timeout=timeout)
elif ds.type == 'oracle':
engine = create_engine(uri,
pool_timeout=timeout)
else:
engine = create_engine(uri, connect_args={"connect_timeout": timeout}, pool_timeout=timeout)
return engine
+3 -2
View File
@@ -472,7 +472,8 @@
"duplicate_email": "Duplicate email",
"interface_credentials": "Interface credentials",
"no_credentials_yet": "No credentials yet",
"intelligent_customer_service": "SQLBot Intelligent Customer Service"
"intelligent_customer_service": "SQLBot Intelligent Customer Service",
"origin_format_error": "Format invalid, starts with http or https and cannot end with /"
},
"chat": {
"type": "Chart Type",
@@ -518,4 +519,4 @@
"back_community": "Restore to Community Edition",
"confirm_tips": "Are you sure to restore to Community Edition?"
}
}
}
+3 -2
View File
@@ -487,7 +487,8 @@
"stop_replying": "停止回答",
"i_am_sqlbot": "你好,我是 SQLBot",
"predict_data_etc": "我可以查询数据、生成图表、检测数据异常、预测数据等",
"intelligent_data_query": "赶快开启智能问数吧~"
"intelligent_data_query": "赶快开启智能问数吧~",
"origin_format_error": "格式错误,http或https开头,不能以 / 结尾"
},
"chat": {
"type": "图表类型",
@@ -533,4 +534,4 @@
"back_community": "还原至社区版",
"confirm_tips": "确定还原至社区版?"
}
}
}
@@ -19,6 +19,7 @@ import icon_copy_outlined from '@/assets/svg/icon_copy_outlined.svg'
import { useI18n } from 'vue-i18n'
import SQLComponent from '@/views/chat/component/SQLComponent.vue'
import { useAssistantStore } from '@/stores/assistant'
import AddViewDashboard from '@/views/dashboard/common/AddViewDashboard.vue'
const props = withDefaults(
defineProps<{
@@ -35,7 +36,7 @@ const props = withDefaults(
)
const { t } = useI18n()
const addViewRef = ref(null)
const emits = defineEmits(['exitFullScreen'])
const dataObject = computed<{
@@ -191,7 +192,8 @@ function showSql() {
}
function addToDashboard() {
console.log('todo')
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
addViewRef.value?.optInit()
}
function copy() {
@@ -303,6 +305,7 @@ function copy() {
/>
</div>
<AddViewDashboard ref="addViewRef"></AddViewDashboard>
<el-dialog
v-if="!enlarge"
v-model="dialogVisible"
@@ -0,0 +1,249 @@
<script lang="ts" setup>
import { onMounted, reactive, ref } from 'vue'
import {
findNextComponentIndex,
saveDashboardResourceTarget,
} from '@/views/dashboard/utils/canvasUtils.ts'
import { useI18n } from 'vue-i18n'
import { dashboardApi } from '@/api/dashboard.ts'
import type { SQTreeNode } from '@/views/dashboard/utils/treeNode.ts'
import cloneDeep from 'lodash/cloneDeep'
import { findNewComponentFromList } from '@/views/dashboard/components/component-list.ts'
import { guid } from '@/utils/canvas.ts'
const { t } = useI18n()
const resource = ref(null)
const optInit = (viewInfo: any) => {
resourceDialogShow.value = true
state.viewInfo = viewInfo
}
const state = reactive({
dashboardList: [] as SQTreeNode[],
viewInfo: null,
})
const resourceDialogShow = ref(false)
const loading = ref(false)
const resourceForm = reactive({
addType: 'history',
dashboardId: '',
dashboardName: '',
})
const resourceFormRulesNew = ref({
dashboardName: [
{
required: true,
min: 1,
max: 64,
message: t('dashboard.length_limit64'),
trigger: 'change',
},
],
})
const resourceFormRulesHistory = ref({
dashboardId: [
{
required: true,
min: 1,
max: 64,
message: '请选择仪表板',
trigger: 'change',
},
],
})
const resetForm = () => {
resourceForm.dashboardId = ''
resourceForm.dashboardName = ''
resourceDialogShow.value = false
}
const saveResourcePrepare = () => {
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
resource.value?.validate((result) => {
if (result) {
const component = cloneDeep(findNewComponentFromList('SQView'))
const newComponentId = guid()
if (resourceForm.addType === 'history' && component) {
findNextComponentIndex({ id: resourceForm.dashboardId }, (result: any) => {
const {
bottomPosition,
dashboardInfo,
canvasDataResult,
canvasStyleResult,
canvasViewInfoPreview,
} = result
const params = {
opt: 'updateLeaf',
pid: 'root',
id: resourceForm.dashboardId,
name: dashboardInfo.name,
}
component['id'] = newComponentId
component['y'] = bottomPosition + 1
canvasDataResult.push(component)
canvasViewInfoPreview[newComponentId] = state.viewInfo
const commonParams = {
componentData: canvasDataResult,
canvasStyleData: canvasStyleResult,
canvasViewInfo: canvasViewInfoPreview,
}
saveResource(params, commonParams)
})
} else if (resourceForm.addType === 'new' && component) {
const params = { opt: 'newLeaf', pid: 'root', name: resourceForm.dashboardName }
component['id'] = newComponentId
const commonParams = {
componentData: [component],
canvasStyleData: {},
canvasViewInfo: { newComponentId: state.viewInfo },
}
saveResource(params, commonParams)
}
}
})
}
const saveResource = (params: any, commonParams: any) => {
saveDashboardResourceTarget(params, commonParams, function () {
const messageTips = t('common.save_success')
ElMessage({
type: 'success',
message: messageTips,
})
resetForm()
})
}
const initDashboardList = () => {
state.dashboardList = []
const params = {}
dashboardApi.list_resource(params).then((res: SQTreeNode[]) => {
state.dashboardList = res || []
})
}
onMounted(() => {
initDashboardList()
})
defineExpose({
optInit,
})
</script>
<template>
<el-dialog
v-model="resourceDialogShow"
class="create-dialog"
title="添加到仪表板"
width="420px"
:before-close="resetForm"
append-to-body
@submit.prevent
>
<el-form
ref="resource"
v-loading="loading"
label-position="top"
require-asterisk-position="right"
:model="resourceForm"
:rules="resourceForm.addType === 'new' ? resourceFormRulesNew : resourceFormRulesHistory"
@submit.prevent
>
<el-form-item label="仪表板名称" required prop="addType">
<el-radio-group v-model="resourceForm.addType">
<el-radio value="history">存量仪表板</el-radio>
<el-radio value="new">新建仪表板</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="resourceForm.addType === 'new'"
label="仪表板"
required
prop="dashboardName"
>
<el-input
v-model="resourceForm.dashboardName"
clearable
placeholder="请输入仪表板名称"
@keydown.stop
@keyup.stop
/>
</el-form-item>
<el-form-item
v-if="resourceForm.addType === 'history'"
label="仪表板"
required
prop="dashboardId"
>
<el-select v-model="resourceForm.dashboardId" placeholder="请选择仪表板">
<el-option
v-for="item in state.dashboardList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button secondary @click="resetForm()">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="saveResourcePrepare()">{{ t('common.confirm') }}</el-button>
</template>
</el-dialog>
</template>
<style lang="less" scoped>
.tree-content {
width: 552px;
height: 380px;
border: 1px solid #dee0e3;
border-radius: 4px;
padding: 8px;
overflow-y: auto;
.empty-search {
width: 100%;
margin-top: 57px;
display: flex;
flex-direction: column;
align-items: center;
img {
width: 100px;
height: 100px;
margin-bottom: 8px;
}
span {
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-weight: 400;
line-height: 22px;
color: #646a73;
}
}
}
.custom-tree-node {
display: flex;
align-items: center;
span {
margin-left: 8.75px;
width: 120px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.custom-tree-folder {
color: rgb(255, 198, 10);
}
</style>
@@ -115,10 +115,6 @@ const propsTree = {
isLeaf: (node) => !node.children?.length,
}
// const showPid = computed(() => {
// return state.opt && ['newLeaf'].includes(state.opt) && state.parentSelect
// })
const showPid = false
const saveResource = () => {
@@ -2,7 +2,6 @@
import ChartComponent from '@/views/chat/component/ChartComponent.vue'
import icon_window_mini_outlined from '@/assets/svg/icon_window-mini_outlined.svg'
import SqViewDisplay from '@/views/dashboard/components/sq-view/index.vue'
import _ from 'lodash'
const props = defineProps({
viewInfo: {
type: Object,
@@ -48,8 +48,39 @@ export const initCanvasData = (params: any, callBack: () => void) => {
})
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export const findNextComponentIndex = async (params: any, callBack: Function) => {
load_resource_prepare(params, function (result: any) {
let bottomPosition = 0
const { dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview } = result
canvasDataResult.forEach((component: any) => {
const componentBottom = component.y + component.sizeY
if (componentBottom > bottomPosition) {
bottomPosition = componentBottom
}
})
callBack({
bottomPosition,
dashboardInfo,
canvasDataResult,
canvasStyleResult,
canvasViewInfoPreview,
})
})
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export const saveDashboardResource = (params: any, callBack: Function) => {
const commonParams = {
componentData: componentData.value,
canvasStyleData: canvasStyleData.value,
canvasViewInfo: canvasViewInfo.value,
}
saveDashboardResourceTarget(params, commonParams, callBack)
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export const saveDashboardResourceTarget = (params: any, commonParams: any, callBack: Function) => {
params['workspace_id'] = workspace_id
dashboardApi.check_name(params).then((resCheck: any) => {
if (resCheck) {
@@ -57,9 +88,9 @@ export const saveDashboardResource = (params: any, callBack: Function) => {
// create canvas
const requestParams = {
...params,
component_data: JSON.stringify(componentData.value),
canvas_style_data: JSON.stringify(canvasStyleData.value),
canvas_view_info: JSON.stringify(canvasViewInfo.value),
component_data: JSON.stringify(commonParams.componentData),
canvas_style_data: JSON.stringify(commonParams.canvasStyleData),
canvas_view_info: JSON.stringify(commonParams.canvasViewInfo),
}
dashboardApi.create_canvas(requestParams).then((res: any) => {
dashboardStore.updateDashboardInfo({
@@ -81,9 +112,9 @@ export const saveDashboardResource = (params: any, callBack: Function) => {
} else if (params.opt === 'updateLeaf') {
const requestParams = {
...params,
component_data: JSON.stringify(componentData.value),
canvas_style_data: JSON.stringify(canvasStyleData.value),
canvas_view_info: JSON.stringify(canvasViewInfo.value),
component_data: JSON.stringify(commonParams.componentData),
canvas_style_data: JSON.stringify(commonParams.canvasStyleData),
canvas_view_info: JSON.stringify(commonParams.canvasViewInfo),
}
dashboardApi.update_canvas(requestParams).then(() => {
callBack()
@@ -245,6 +245,11 @@ const rules = {
t('datasource.please_enter') + t('common.empty') + t('embedded.cross_domain_settings'),
trigger: 'blur',
},
{
pattern: /^(https?:\/\/)?([\w-]+\.)*[\w-]+(:\d+)?$/,
message: t('embedded.origin_format_error'),
trigger: 'blur',
},
],
}