mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
perf: Place the assistant menu in the workspace
This commit is contained in:
committed by
fit2cloud-chenyw
parent
b8b3727d17
commit
e54a5fe46f
@@ -0,0 +1,48 @@
|
||||
"""061_assistant_oid_ddl
|
||||
|
||||
Revision ID: 547df942eb90
|
||||
Revises: b40e41c67db3
|
||||
Create Date: 2026-01-09 15:02:19.891766
|
||||
|
||||
"""
|
||||
import json
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel.sql.sqltypes
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '547df942eb90'
|
||||
down_revision = 'b40e41c67db3'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
op.add_column('sys_assistant', sa.Column('oid', sa.BigInteger(), nullable=True))
|
||||
conn = op.get_bind()
|
||||
conn.execute(sa.text("UPDATE sys_assistant SET oid = 1 WHERE oid IS NULL"))
|
||||
rows = conn.execute(
|
||||
sa.text("SELECT id, configuration FROM sys_assistant WHERE type = 0 AND configuration IS NOT NULL")
|
||||
)
|
||||
for row in rows:
|
||||
try:
|
||||
config = json.loads(row.configuration) if isinstance(row.configuration, str) else row.configuration
|
||||
oid_value = config.get('oid', 1) if isinstance(config, dict) else 1
|
||||
if isinstance(oid_value, int) and oid_value != 1:
|
||||
conn.execute(
|
||||
sa.text("UPDATE sys_assistant SET oid = :oid WHERE id = :id"),
|
||||
{"oid": oid_value, "id": row.id}
|
||||
)
|
||||
except (json.JSONDecodeError, TypeError, AttributeError):
|
||||
pass
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('sys_assistant', 'oid')
|
||||
# ### end Alembic commands ###
|
||||
@@ -15,7 +15,7 @@ from apps.system.models.system_model import AssistantModel
|
||||
from apps.system.schemas.auth import CacheName, CacheNamespace
|
||||
from apps.system.schemas.system_schema import AssistantBase, AssistantDTO, AssistantUiSchema, AssistantValidator
|
||||
from common.core.config import settings
|
||||
from common.core.deps import SessionDep, Trans
|
||||
from common.core.deps import SessionDep, Trans, CurrentUser
|
||||
from common.core.security import create_access_token
|
||||
from common.core.sqlbot_cache import clear_cache
|
||||
from common.utils.utils import get_origin_from_referer, origin_match_domain
|
||||
@@ -165,8 +165,8 @@ async def clear_ui_cache(id: int):
|
||||
|
||||
|
||||
@router.get("", response_model=list[AssistantModel], summary=f"{PLACEHOLDER_PREFIX}assistant_grid_api", description=f"{PLACEHOLDER_PREFIX}assistant_grid_api")
|
||||
async def query(session: SessionDep):
|
||||
list_result = session.exec(select(AssistantModel).where(AssistantModel.type != 4).order_by(AssistantModel.name,
|
||||
async def query(session: SessionDep, current_user: CurrentUser):
|
||||
list_result = session.exec(select(AssistantModel).where(AssistantModel.oid == current_user.oid, AssistantModel.type != 4).order_by(AssistantModel.name,
|
||||
AssistantModel.create_time)).all()
|
||||
return list_result
|
||||
|
||||
@@ -180,8 +180,9 @@ async def query_advanced_application(session: SessionDep):
|
||||
|
||||
@router.post("", summary=f"{PLACEHOLDER_PREFIX}assistant_create_api", description=f"{PLACEHOLDER_PREFIX}assistant_create_api")
|
||||
@system_log(LogConfig(operation_type=OperationType.CREATE, module=OperationModules.APPLICATION, result_id_expr="id"))
|
||||
async def add(request: Request, session: SessionDep, creator: AssistantBase):
|
||||
return await save(request, session, creator)
|
||||
async def add(request: Request, session: SessionDep, current_user: CurrentUser, creator: AssistantBase):
|
||||
oid = current_user.oid if creator.type != 4 else 1
|
||||
return await save(request, session, creator, oid)
|
||||
|
||||
|
||||
@router.put("", summary=f"{PLACEHOLDER_PREFIX}assistant_update_api", description=f"{PLACEHOLDER_PREFIX}assistant_update_api")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
|
||||
from typing import Optional
|
||||
from fastapi import FastAPI, Request
|
||||
from sqlmodel import Session, select
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
@@ -32,9 +33,10 @@ def dynamic_upgrade_cors(request: Request, session: Session):
|
||||
cors_middleware.kwargs['allow_origins'] = updated_origins
|
||||
|
||||
|
||||
async def save(request: Request, session: Session, creator: AssistantBase):
|
||||
async def save(request: Request, session: Session, creator: AssistantBase, oid: Optional[int] = 1):
|
||||
db_model = AssistantModel.model_validate(creator)
|
||||
db_model.create_time = get_timestamp()
|
||||
db_model.oid = oid
|
||||
session.add(db_model)
|
||||
session.commit()
|
||||
dynamic_upgrade_cors(request=request, session=session)
|
||||
|
||||
@@ -52,6 +52,7 @@ class AssistantBaseModel(SQLModel):
|
||||
create_time: int = Field(default=0, sa_type=BigInteger())
|
||||
app_id: Optional[str] = Field(default=None, max_length=255, nullable=True)
|
||||
app_secret: Optional[str] = Field(default=None, max_length=255, nullable=True)
|
||||
oid: Optional[int] = Field(nullable=True, sa_type=BigInteger(), default=1)
|
||||
|
||||
class AssistantModel(SnowflakeBase, AssistantBaseModel, table=True):
|
||||
__tablename__ = "sys_assistant"
|
||||
|
||||
@@ -668,7 +668,8 @@
|
||||
"welcome_description": "Welcome Description",
|
||||
"data_analysis_now": "I can query data, generate charts, detect data anomalies, predict data, and more! Enable Smart Data Analysis now!",
|
||||
"window_entrance_icon": "Floating window entrance icon",
|
||||
"preview_error": "The current page prohibits embedding using Iframe!"
|
||||
"preview_error": "The current page prohibits embedding using Iframe!",
|
||||
"assistant_app": "Assistant App"
|
||||
},
|
||||
"chat": {
|
||||
"type": "Chart Type",
|
||||
|
||||
@@ -668,7 +668,8 @@
|
||||
"welcome_description": "환영 메시지 설명",
|
||||
"data_analysis_now": "저는 데이터 조회, 차트 생성, 데이터 이상 감지, 데이터 예측 등을 할 수 있습니다. 빨리 스마트 데이터 조회를 시작하세요~",
|
||||
"window_entrance_icon": "플로팅 윈도우 입구 아이콘",
|
||||
"preview_error": "현재 페이지는 Iframe 임베딩을 허용하지 않습니다!"
|
||||
"preview_error": "현재 페이지는 Iframe 임베딩을 허용하지 않습니다!",
|
||||
"assistant_app": "어시스턴트 앱"
|
||||
},
|
||||
"chat": {
|
||||
"type": "차트 유형",
|
||||
|
||||
@@ -668,7 +668,8 @@
|
||||
"welcome_description": "欢迎语描述",
|
||||
"data_analysis_now": "我可以查询数据、生成图表、检测数据异常、预测数据等赶快开启智能问数吧~",
|
||||
"window_entrance_icon": "浮窗入口图标",
|
||||
"preview_error": "当前页面禁止使用 Iframe 嵌入!"
|
||||
"preview_error": "当前页面禁止使用 Iframe 嵌入!",
|
||||
"assistant_app": "小助手应用"
|
||||
},
|
||||
"chat": {
|
||||
"type": "图表类型",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import LayoutDsl from '@/components/layout/LayoutDsl.vue'
|
||||
|
||||
import Datasource from '@/views/ds/Datasource.vue'
|
||||
import SetAssistant from '@/views/system/embedded/iframe.vue'
|
||||
import { i18n } from '@/i18n'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import type { Router } from 'vue-router'
|
||||
@@ -22,6 +23,13 @@ const dynamicRouterList = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
parent: 'set',
|
||||
path: '/set/assistant',
|
||||
name: 'setAssistant',
|
||||
component: SetAssistant,
|
||||
meta: { title: t('embedded.assistant_app') },
|
||||
},
|
||||
] as any[]
|
||||
|
||||
const reduceRouters = (router: Router, invalid_router_name_list: string[]) => {
|
||||
@@ -71,7 +79,17 @@ const reduceRouters = (router: Router, invalid_router_name_list: string[]) => {
|
||||
|
||||
export const generateDynamicRouters = (router: Router) => {
|
||||
if (userStore.isAdmin || userStore.isSpaceAdmin) {
|
||||
dynamicRouterList.forEach((item: any) => router.addRoute(item))
|
||||
dynamicRouterList.forEach((item: any) => {
|
||||
if (!item.parent) {
|
||||
router.addRoute(item)
|
||||
} else {
|
||||
router.addRoute(item.parent, item)
|
||||
const parentRoute: any = router.getRoutes().find((r: any) => r.name === item.parent)
|
||||
if (parentRoute?.children) {
|
||||
parentRoute.children.push(item)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const router_name_list = [] as string[]
|
||||
const stack = [...dynamicRouterList]
|
||||
|
||||
@@ -9,7 +9,10 @@ import DashboardEditor from '@/views/dashboard/editor/index.vue'
|
||||
import DashboardPreview from '@//views/dashboard/preview/SQPreviewSingle.vue'
|
||||
import Dashboard from '@/views/dashboard/index.vue'
|
||||
import Model from '@/views/system/model/Model.vue'
|
||||
import Embedded from '@/views/system/embedded/index.vue'
|
||||
// import Embedded from '@/views/system/embedded/index.vue'
|
||||
// import SetAssistant from '@/views/system/embedded/iframe.vue'
|
||||
import SystemEmbedded from '@/views/system/embedded/Page.vue'
|
||||
|
||||
import assistantTest from '@/views/system/embedded/Test.vue'
|
||||
import assistant from '@/views/embedded/index.vue'
|
||||
import EmbeddedPage from '@/views/embedded/page.vue'
|
||||
@@ -116,6 +119,12 @@ export const routes = [
|
||||
component: Permission,
|
||||
meta: { title: t('workspace.permission_configuration') },
|
||||
},
|
||||
/* {
|
||||
path: '/set/assistant',
|
||||
name: 'setAssistant',
|
||||
component: SetAssistant,
|
||||
meta: { title: t('embedded.assistant_app') },
|
||||
}, */
|
||||
{
|
||||
path: '/set/professional',
|
||||
name: 'professional',
|
||||
@@ -184,7 +193,7 @@ export const routes = [
|
||||
{
|
||||
path: 'embedded',
|
||||
name: 'embedded',
|
||||
component: Embedded,
|
||||
component: SystemEmbedded,
|
||||
meta: {
|
||||
title: t('embedded.embedded_management'),
|
||||
iconActive: 'embedded',
|
||||
|
||||
@@ -39,7 +39,7 @@ export const watchRouter = (router: Router) => {
|
||||
if (!userStore.getUid) {
|
||||
await userStore.info()
|
||||
generateDynamicRouters(router)
|
||||
isFirstDynamicPath = to?.path === '/ds/index'
|
||||
isFirstDynamicPath = to?.path && ['/ds/index', '/set/assistant'].includes(to.path)
|
||||
if (isFirstDynamicPath) {
|
||||
next({ ...to, replace: true })
|
||||
return
|
||||
|
||||
@@ -31,7 +31,7 @@ interface Form {
|
||||
description?: string | null
|
||||
}
|
||||
|
||||
const emits = defineEmits(['btnSelectChange'])
|
||||
// const emits = defineEmits(['btnSelectChange'])
|
||||
const { t } = useI18n()
|
||||
const multipleSelectionAll = ref<any[]>([])
|
||||
const keywords = ref('')
|
||||
@@ -313,7 +313,8 @@ const copyCode = (row: any, key: any = 'app_secret') => {
|
||||
<template>
|
||||
<div v-loading="searchLoading" class="embedded-page">
|
||||
<div class="tool-left">
|
||||
<div class="btn-select">
|
||||
<span class="page-title">{{ t('embedded.embedded_management') }}</span>
|
||||
<!-- <div class="btn-select">
|
||||
<el-button
|
||||
:class="[btnSelect === 'd' && 'is-active']"
|
||||
text
|
||||
@@ -328,7 +329,7 @@ const copyCode = (row: any, key: any = 'app_secret') => {
|
||||
>
|
||||
{{ t('embedded.embedded_page') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div> -->
|
||||
<div>
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
@@ -642,7 +643,11 @@ const copyCode = (row: any, key: any = 'app_secret') => {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.page-title {
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
.btn-select {
|
||||
height: 32px;
|
||||
padding-left: 4px;
|
||||
|
||||
@@ -19,7 +19,9 @@ import DsCard from './DsCard.vue'
|
||||
import { getList, updateAssistant, saveAssistant, delOne, dsApi } from '@/api/embedded'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { useUserStore } from '@/stores/user.ts'
|
||||
|
||||
const userStore = useUserStore()
|
||||
defineProps({
|
||||
btnSelect: {
|
||||
type: String,
|
||||
@@ -27,7 +29,7 @@ defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits(['btnSelectChange'])
|
||||
// const emits = defineEmits(['btnSelectChange'])
|
||||
|
||||
const { t } = useI18n()
|
||||
const { copy } = useClipboard({ legacy: true })
|
||||
@@ -49,7 +51,6 @@ const activeMode = ref('full')
|
||||
const embeddedList = ref<any[]>([])
|
||||
const systemCredentials = ['localStorage', 'custom', 'cookie', 'sessionStorage']
|
||||
const credentials = ['header', 'cookie', 'param']
|
||||
const workspaces = ref<any[]>([])
|
||||
|
||||
const defaultEmbedded = {
|
||||
id: '',
|
||||
@@ -112,11 +113,7 @@ const userTypeList = [
|
||||
value: 1,
|
||||
},
|
||||
]
|
||||
const initWorkspace = () => {
|
||||
workspaceList().then((res) => {
|
||||
workspaces.value = res
|
||||
})
|
||||
}
|
||||
|
||||
const handleAddEmbedded = (val: any) => {
|
||||
Object.assign(currentEmbedded, cloneDeep(defaultEmbedded))
|
||||
Object.keys(dsForm).forEach((ele) => {
|
||||
@@ -132,11 +129,7 @@ const handleAddEmbedded = (val: any) => {
|
||||
handleAdvancedEmbedded(null)
|
||||
}
|
||||
}
|
||||
const wsChanged = (val: any) => {
|
||||
dsForm.public_list = []
|
||||
dsForm.oid = val
|
||||
getDsList()
|
||||
}
|
||||
|
||||
const getDsList = () => {
|
||||
dsApi(dsForm.oid).then((res: any) => {
|
||||
dsListOptions.value = res || []
|
||||
@@ -144,9 +137,10 @@ const getDsList = () => {
|
||||
}
|
||||
const handleBaseEmbedded = (row: any) => {
|
||||
advancedApplication.value = false
|
||||
initWorkspace()
|
||||
if (row) {
|
||||
Object.assign(dsForm, JSON.parse(row.configuration))
|
||||
} else {
|
||||
Object.assign(dsForm, { oid: userStore.getOid })
|
||||
}
|
||||
getDsList()
|
||||
ruleConfigvVisible.value = true
|
||||
@@ -557,7 +551,8 @@ const saveHandler = () => {
|
||||
<template>
|
||||
<div v-loading="searchLoading" class="embedded-index no-padding">
|
||||
<div class="tool-left">
|
||||
<div class="btn-select">
|
||||
<span class="page-title">{{ t('embedded.assistant_app') }}</span>
|
||||
<!-- <div class="btn-select">
|
||||
<el-button
|
||||
:class="[btnSelect === 'd' && 'is-active']"
|
||||
text
|
||||
@@ -572,7 +567,7 @@ const saveHandler = () => {
|
||||
>
|
||||
{{ t('embedded.embedded_page') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div> -->
|
||||
<div>
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
@@ -922,7 +917,7 @@ const saveHandler = () => {
|
||||
class="form-content_error"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item prop="oid" :label="t('user.workspace')">
|
||||
<!-- <el-form-item prop="oid" :label="t('user.workspace')">
|
||||
<el-select
|
||||
v-model="dsForm.oid"
|
||||
filterable
|
||||
@@ -938,7 +933,7 @@ const saveHandler = () => {
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
|
||||
<el-form-item class="private-list_form">
|
||||
<template #label>
|
||||
@@ -1166,6 +1161,11 @@ const saveHandler = () => {
|
||||
margin-bottom: 16px;
|
||||
padding: 0 24px 0 24px;
|
||||
|
||||
.page-title {
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
.title {
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
|
||||
Reference in New Issue
Block a user