mirror of
https://github.com/dataease/dataease.git
synced 2026-06-13 09:04:38 +08:00
feat: 仪表板支持问数 (#17216)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package io.dataease.chart.dao.ext.mapper;
|
||||
|
||||
import io.dataease.api.chart.vo.ViewSelectorVO;
|
||||
import io.dataease.api.dataset.vo.DataSQLBotDatasetVO;
|
||||
import io.dataease.chart.dao.auto.entity.CoreChartView;
|
||||
import io.dataease.chart.dao.ext.entity.ChartBasePO;
|
||||
import io.dataease.extensions.view.dto.ChartViewDTO;
|
||||
@@ -35,4 +36,30 @@ public interface ExtChartViewMapper {
|
||||
LIMIT 1
|
||||
""")
|
||||
ChartViewDTO findChartViewAround(@Param("viewId") String viewId);
|
||||
|
||||
|
||||
@Select("""
|
||||
select DISTINCT table_id from core_chart_view_snapshot where scene_id=#{dvId}
|
||||
""")
|
||||
List<Long> findDatasetGroupIdByDvId(@Param("dvId") String dvId);
|
||||
|
||||
|
||||
@Select("""
|
||||
SELECT
|
||||
DISTINCT
|
||||
sdg.id AS table_id,
|
||||
sdg.NAME AS table_name,
|
||||
cd.id AS ds_id,
|
||||
cd.NAME AS ds_name\s
|
||||
FROM
|
||||
core_dataset_table sdt
|
||||
INNER JOIN core_datasource cd ON sdt.datasource_id = cd.id
|
||||
INNER JOIN core_dataset_group sdg ON sdt.dataset_group_id = sdg.id
|
||||
INNER JOIN snapshot_core_chart_view sccv on sccv.table_id = sdt.dataset_group_id\s
|
||||
WHERE
|
||||
sccv.scene_id = #{dvId}
|
||||
""")
|
||||
List<DataSQLBotDatasetVO> findDataSQLBotDatasetDvId(@Param("dvId") String dvId);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,12 +6,15 @@ import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
|
||||
import io.dataease.api.dataset.union.DatasetTableInfoDTO;
|
||||
import io.dataease.api.dataset.union.UnionDTO;
|
||||
import io.dataease.api.dataset.vo.DataSQLBotAssistantVO;
|
||||
import io.dataease.api.dataset.vo.DataSQLBotDatasetVO;
|
||||
import io.dataease.api.dataset.vo.SQLBotAssistanTable;
|
||||
import io.dataease.api.dataset.vo.SQLBotAssistantField;
|
||||
import io.dataease.api.permissions.dataset.api.ColumnPermissionsApi;
|
||||
import io.dataease.api.permissions.dataset.dto.DataSetColumnPermissionsDTO;
|
||||
import io.dataease.api.permissions.dataset.dto.DataSetRowPermissionsTreeDTO;
|
||||
import io.dataease.auth.bo.TokenUserBO;
|
||||
import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper;
|
||||
import io.dataease.chart.dao.ext.mapper.ExtChartViewMapper;
|
||||
import io.dataease.commons.utils.EncryptUtils;
|
||||
import io.dataease.constant.ColumnPermissionConstants;
|
||||
import io.dataease.dataset.dao.auto.entity.CoreDatasetGroup;
|
||||
@@ -68,6 +71,9 @@ public class DatasetSQLBotManage {
|
||||
@Resource
|
||||
private DataSetAssistantMapper dataSetAssistantMapper;
|
||||
|
||||
@Resource
|
||||
private ExtChartViewMapper extChartViewMapper;
|
||||
|
||||
@Resource
|
||||
private EngineManage engineManage;
|
||||
|
||||
@@ -147,8 +153,12 @@ public class DatasetSQLBotManage {
|
||||
return datasetRowPermissions.stream().collect(Collectors.groupingBy(DataSetRowPermissionsTreeDTO::getDatasetId));
|
||||
}
|
||||
|
||||
public List<DataSQLBotDatasetVO> getDatasetList(String dvInfo){
|
||||
return extChartViewMapper.findDataSQLBotDatasetDvId(dvInfo);
|
||||
}
|
||||
|
||||
public List<DataSQLBotAssistantVO> getDatasourceList(Long dsId, Long datasetId) {
|
||||
|
||||
public List<DataSQLBotAssistantVO> getDatasourceList(Long dsId, Long datasetId, String dvInfo) {
|
||||
TokenUserBO user = Objects.requireNonNull(AuthUtils.getUser());
|
||||
Long oid = user.getDefaultOid();
|
||||
Long uid = user.getUserId();
|
||||
@@ -165,6 +175,12 @@ public class DatasetSQLBotManage {
|
||||
if (ObjectUtils.isNotEmpty(dsId)) {
|
||||
queryWrapper.eq("cd.id", dsId);
|
||||
}
|
||||
if(ObjectUtils.isNotEmpty(dvInfo)){
|
||||
List<Long> targetDsGroupIds = extChartViewMapper.findDatasetGroupIdByDvId(dvInfo);
|
||||
if(CollectionUtils.isNotEmpty(targetDsGroupIds)){
|
||||
queryWrapper.in("cdg.id", targetDsGroupIds);
|
||||
}
|
||||
}
|
||||
if (ObjectUtils.isEmpty(model)) {
|
||||
if (!isAdmin) {
|
||||
return null;
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.dataease.dataset.server;
|
||||
|
||||
import io.dataease.api.dataset.DataAssistantApi;
|
||||
import io.dataease.api.dataset.vo.DataSQLBotAssistantVO;
|
||||
import io.dataease.api.dataset.vo.DataSQLBotDatasetVO;
|
||||
import io.dataease.dataset.manage.DatasetSQLBotManage;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -16,7 +17,12 @@ public class DatasetSQLBotServer implements DataAssistantApi {
|
||||
@Resource
|
||||
private DatasetSQLBotManage datasetSQLBotManage;
|
||||
@Override
|
||||
public List<DataSQLBotAssistantVO> getDatasourceList(Long dsId, Long datasetId) {
|
||||
return datasetSQLBotManage.getDatasourceList(dsId, datasetId);
|
||||
public List<DataSQLBotAssistantVO> getDatasourceList(Long dsId, Long datasetId, String dvInfo) {
|
||||
return datasetSQLBotManage.getDatasourceList(dsId, datasetId,dvInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DataSQLBotDatasetVO> getDatasetList(String dvInfo) {
|
||||
return datasetSQLBotManage.getDatasetList(dvInfo);
|
||||
}
|
||||
}
|
||||
|
||||
BIN
core/core-frontend/public/api.png
Normal file
BIN
core/core-frontend/public/api.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
3
core/core-frontend/src/api/aiSqlBot.ts
Normal file
3
core/core-frontend/src/api/aiSqlBot.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export const findDvSqlBotDataset = dvInfo => request.get({ url: '/sqlbot/dataset/' + dvInfo })
|
||||
@@ -2929,6 +2929,7 @@ export default {
|
||||
column_name: 'Field name'
|
||||
},
|
||||
visualization: {
|
||||
cur_sq_dataset: 'Currently selected dataset:',
|
||||
data_match_type: 'Data match type',
|
||||
select_resource: 'Select {0}',
|
||||
change_screen_page: 'Change {0}',
|
||||
|
||||
@@ -2848,6 +2848,7 @@ export default {
|
||||
column_name: '欄位名稱'
|
||||
},
|
||||
visualization: {
|
||||
cur_sq_dataset: '當前選擇的數據集:',
|
||||
data_match_type: '資料匹配方式',
|
||||
select_resource: '請選擇{0}',
|
||||
change_screen_page: '更換{0}',
|
||||
|
||||
@@ -2857,6 +2857,7 @@ export default {
|
||||
column_name: '字段名称'
|
||||
},
|
||||
visualization: {
|
||||
cur_sq_dataset: '当前选择的数据集:',
|
||||
data_match_type: '数据匹配方式',
|
||||
select_resource: '请选择{0}',
|
||||
change_screen_page: '更换{0}',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const suffix = `${import.meta.env.VITE_VERSION}-dataease`
|
||||
|
||||
const dom = document.querySelector('head')
|
||||
const cb = dom.appendChild.bind(dom)
|
||||
|
||||
const formatterUrl = <T extends Node>(node: T, prefix: string) => {
|
||||
if (['SCRIPT', 'LINK'].includes(node.nodeName)) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import eventBus from '@/utils/eventBus'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import DashboardHiddenComponent from '@/components/dashboard/DashboardHiddenComponent.vue'
|
||||
import { recoverToPublished } from '@/api/visualization/dataVisualization'
|
||||
import SqlAssistant from '@/views/sqlbot/assistant.vue'
|
||||
const embeddedStore = useEmbedded()
|
||||
const { wsCache } = useCache()
|
||||
const canvasCacheOutRef = ref(null)
|
||||
@@ -338,6 +339,7 @@ onUnmounted(() => {
|
||||
:class="{ 'preview-content': editMode === 'preview' }"
|
||||
element-loading-background="rgba(0, 0, 0, 0)"
|
||||
>
|
||||
<SqlAssistant></SqlAssistant>
|
||||
<!-- 中间画布 -->
|
||||
<main class="center" :class="{ 'de-screen-full': fullscreenFlag }">
|
||||
<de-canvas
|
||||
|
||||
63
core/core-frontend/src/views/sqlbot/Inactivate.vue
Normal file
63
core/core-frontend/src/views/sqlbot/Inactivate.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="inactivate-div">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<div class="inactivate-button el-dropdown-link" :style="{ color: fontColor }">
|
||||
API 功能
|
||||
<img class="api-img" src="/api.png" alt="inactivate" />
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="showHistory">历史记录(显/隐)</el-dropdown-item>
|
||||
<el-dropdown-item command="newChat" divided>新建对话</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElIcon } from 'element-plus-secondary'
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
|
||||
defineProps({
|
||||
fontColor: {
|
||||
type: String,
|
||||
default: '#000000'
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['showHistory', 'newChat'])
|
||||
const handleCommand = (command: string) => {
|
||||
if (command === 'showHistory') {
|
||||
emit('showHistory')
|
||||
} else if (command === 'newChat') {
|
||||
emit('newChat')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.inactivate-div {
|
||||
position: absolute;
|
||||
right: 32px;
|
||||
top: 92px;
|
||||
height: auto;
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.inactivate-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
.api-img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
</style>
|
||||
77
core/core-frontend/src/views/sqlbot/SQDatasetSelect.vue
Normal file
77
core/core-frontend/src/views/sqlbot/SQDatasetSelect.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import { findDvSqlBotDataset } from '@/api/aiSqlBot'
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
const { t } = useI18n()
|
||||
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { dvInfo } = storeToRefs(dvMainStore)
|
||||
const init = () => {
|
||||
if (dvInfo.value.id) {
|
||||
findDvSqlBotDataset(dvInfo.value.id).then(res => {
|
||||
state.baseDatasetInfo = res.data
|
||||
state.curDatasetInfo = state.baseDatasetInfo[0]
|
||||
datasetSelect()
|
||||
})
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
baseDatasetInfo: [],
|
||||
curDatasetInfo: null,
|
||||
curDatasetId: null
|
||||
})
|
||||
|
||||
const datasetSelect = () => {
|
||||
localStorage.setItem('dsId', state.curDatasetInfo.dsId)
|
||||
localStorage.setItem('tableId', state.curDatasetInfo.tableId)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-row class="de-sq-assistant">
|
||||
<span class="de-sq-tips">{{ t('visualization.cur_sq_dataset') }}</span>
|
||||
<el-select
|
||||
v-model="state.curDatasetInfo"
|
||||
class="de-sq-select"
|
||||
:teleported="false"
|
||||
size="small"
|
||||
@change="datasetSelect"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in state.baseDatasetInfo"
|
||||
size="mini"
|
||||
:key="option.tableId"
|
||||
:value="option"
|
||||
:label="option.tableName"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.de-sq-assistant {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
bottom: 140px;
|
||||
left: 20px;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
color: #646a73;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.de-sq-tips {
|
||||
font-size: 14px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.de-sq-select {
|
||||
width: 150px;
|
||||
}
|
||||
</style>
|
||||
131
core/core-frontend/src/views/sqlbot/assistant.vue
Normal file
131
core/core-frontend/src/views/sqlbot/assistant.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div
|
||||
id="dataease-v2-embedded-assistant-sqlbot"
|
||||
class="dataease-v2-embedded-assistant-sqlbot"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createApp, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||
import request from '@/config/axios'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
import SQDatasetSelect from '@/views/sqlbot/SQDatasetSelect.vue'
|
||||
const userStore = useUserStoreWithOut()
|
||||
const loading = ref(true)
|
||||
const state = reactive({
|
||||
domain: '',
|
||||
id: '',
|
||||
enabled: false,
|
||||
valid: false,
|
||||
historyShow: false
|
||||
})
|
||||
const sqlbotExist = ref(false)
|
||||
const timer = ref()
|
||||
|
||||
const loadSqlbotInfo = () => {
|
||||
const url = '/sysParameter/sqlbot'
|
||||
request.get({ url }).then(res => {
|
||||
if (res && res.data) {
|
||||
const { domain, id, enabled, valid } = res.data
|
||||
if (!enabled) {
|
||||
console.error('sqlbot embedded disabled')
|
||||
}
|
||||
if (!valid) {
|
||||
console.error('sqlbot embedded invalid')
|
||||
}
|
||||
state.domain = domain
|
||||
state.id = id
|
||||
state.enabled = enabled
|
||||
state.valid = valid
|
||||
loadSqlbotPage()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const loadSqlbotPage = () => {
|
||||
const scriptId = `sqlbot-assistant-float-script-${state.assistantId}`
|
||||
const exitsScript = document.getElementById(scriptId)
|
||||
if (exitsScript && window['sqlbot_assistant_handler']) {
|
||||
mountedEmbeddedPage()
|
||||
return
|
||||
}
|
||||
console.log('==test==0=')
|
||||
const script = document.createElement('script')
|
||||
script.defer = true
|
||||
script.async = true
|
||||
script.id = scriptId
|
||||
let sqlbotDomain = state.domain
|
||||
if (sqlbotDomain.endsWith('/')) {
|
||||
sqlbotDomain = sqlbotDomain.slice(0, -1)
|
||||
}
|
||||
script.src = `${sqlbotDomain}/assistant.js?id=${state.id}&online=true&userFlag=${
|
||||
userStore.getUid
|
||||
}&t=${new Date().getTime()}`
|
||||
script.onload = () => {
|
||||
console.log('==test==00=')
|
||||
mountedEmbeddedPage()
|
||||
}
|
||||
document.head.appendChild(script)
|
||||
}
|
||||
const mountedEmbeddedPage = () => {
|
||||
if (sqlbotExist.value) {
|
||||
return
|
||||
}
|
||||
const tempTimer = setTimeout(() => {
|
||||
console.log('==test==1=' + window['sqlbot_assistant_handler'])
|
||||
if (window['sqlbot_assistant_handler']) {
|
||||
console.log('==test==2=')
|
||||
|
||||
// window['sqlbot_assistant_handler'].mounted('#dataease-v2-embedded-assistant-sqlbot', {
|
||||
// embeddedId: state.id,
|
||||
// online: true,
|
||||
// userFlag: userStore.getUid
|
||||
// })
|
||||
|
||||
const container = document.getElementById('sqlbot-assistant-chat-container')
|
||||
if (container) {
|
||||
const mountPoint = document.createElement('div')
|
||||
mountPoint.id = 'chat-component-mount-point'
|
||||
container.appendChild(mountPoint)
|
||||
const chatApp = createApp(SQDatasetSelect)
|
||||
chatApp.mount(mountPoint)
|
||||
}
|
||||
loading.value = false
|
||||
sqlbotExist.value = true
|
||||
if (tempTimer) {
|
||||
clearTimeout(tempTimer)
|
||||
}
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadSqlbotInfo()
|
||||
timer.value = setInterval(() => {
|
||||
loadSqlbotInfo()
|
||||
}, 30000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer.value) {
|
||||
clearInterval(timer.value)
|
||||
timer.value = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
#sqlbot-assistant-chat-container {
|
||||
z-index: 200;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.dataease-v2-embedded-assistant-sqlbot {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
@@ -70,6 +70,7 @@ const mountedEmbeddedPage = () => {
|
||||
}
|
||||
const tempTimer = setTimeout(() => {
|
||||
if (window['sqlbot_embedded_handler']) {
|
||||
console.log('===test===111')
|
||||
window['sqlbot_embedded_handler'].mounted('#dataease-v2-embedded-sqlbot', {
|
||||
embeddedId: state.id,
|
||||
online: true,
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package io.dataease.api.dataset;
|
||||
|
||||
import io.dataease.api.dataset.vo.DataSQLBotAssistantVO;
|
||||
import io.dataease.api.dataset.vo.DataSQLBotDatasetVO;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DataAssistantApi {
|
||||
@GetMapping("/datasource")
|
||||
List<DataSQLBotAssistantVO> getDatasourceList(@RequestParam(required = false) Long dsId, @RequestParam(required = false) Long datasetId);
|
||||
List<DataSQLBotAssistantVO> getDatasourceList(@RequestParam(required = false) Long dsId, @RequestParam(required = false) Long datasetId, @RequestParam(required = false) String dvInfo);
|
||||
|
||||
@GetMapping("/dataset/{dvInfo}")
|
||||
List<DataSQLBotDatasetVO> getDatasetList(@PathVariable String dvInfo);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.dataease.api.dataset.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class DataSQLBotDatasetVO implements Serializable {
|
||||
|
||||
private Long tableId;
|
||||
|
||||
private String tableName;
|
||||
|
||||
private String dsId;
|
||||
|
||||
private String dsName;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user