6 changed files with 838 additions and 1 deletions
@ -0,0 +1,65 @@ |
|||
import request from '@/utils/request'; |
|||
import axios from 'axios'; |
|||
/** |
|||
* 获取设备生命周期系统的设备档案树 |
|||
*/ |
|||
export function getDevicesTree() { |
|||
return request({ |
|||
url: "/aibot/devices/tree", |
|||
method: "get", |
|||
}); |
|||
} |
|||
|
|||
|
|||
export function getDevicesMonitors(data:{deviceCode:string,rows:number,page:number}) { |
|||
return axios.post( import.meta.env.VITE_APP_BASE_API+"/aibot/devices/ep_monitors",data); |
|||
} |
|||
|
|||
// 获取ChatBI列表
|
|||
export function getChatBIList() { |
|||
return request({ |
|||
url: '/aibot/chatbi/list', |
|||
method: 'get', |
|||
}); |
|||
} |
|||
|
|||
// 创建ChatBI
|
|||
export function newChatBI(data: any) { |
|||
return request({ |
|||
url: '/aibot/chatbi/new', |
|||
method: 'post', |
|||
data: data |
|||
}); |
|||
} |
|||
|
|||
// 编辑ChatBI
|
|||
export function editChatBI(data: any) { |
|||
return request({ |
|||
url: '/aibot/chatbi/edit', |
|||
method: 'post', |
|||
data: data |
|||
}); |
|||
} |
|||
|
|||
// 删除ChatBI
|
|||
export function delChatBI(data: any) { |
|||
return request({ |
|||
url: '/aibot/chatbi/del', |
|||
method: 'post', |
|||
data: data |
|||
}); |
|||
} |
|||
|
|||
export interface askChatBIRequest { |
|||
user_query:string; |
|||
db_type?:string; |
|||
db_config?:Object; |
|||
} |
|||
// 调用chatbi
|
|||
export function askChatBI(data: askChatBIRequest, mode:string) { |
|||
return request({ |
|||
url: '/aibot/chatbi/api?mode='+mode, |
|||
method: 'post', |
|||
data: data |
|||
}); |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
<!-- |
|||
@ 时间: 2025-12-30 |
|||
@ 备注: chatbi 服务配置界面 |
|||
--> |
|||
|
|||
<script setup lang="ts"> |
|||
import device_panel from './device_panel.vue' |
|||
import ask_panel from './chatbit_ask_panel.vue' |
|||
import {getChatBIList,newChatBI,editChatBI,delChatBI} from "@/api/date/chatbi" |
|||
import { h } from 'vue' |
|||
import { Delete,Edit } from '@element-plus/icons-vue' |
|||
import { ElSelect,ElOption, ElInput,ElMessage,ElMessageBox } from 'element-plus' |
|||
import Chatbi_setting_panel from './chatbi_setting_panel.vue'; |
|||
const dataTable=ref([]) |
|||
const dynamicVNode = ref<VNode | null>(null) |
|||
const currentHoverRow=ref("") //table 行的按钮控制 |
|||
|
|||
const onChatBISetting=()=>{ |
|||
dynamicVNode.value=h(device_panel,{ |
|||
checked:[], |
|||
confirmFunc:(data:string[])=>{ |
|||
}, |
|||
closeFunc:()=>dynamicVNode.value=null |
|||
}) |
|||
} |
|||
const handleMouseEnter=(row:any)=>{ |
|||
currentHoverRow.value=row.uuid |
|||
} |
|||
|
|||
const onChatBIEditOrNew=(row:any|null)=>{ |
|||
const isEdit=row!==null |
|||
const newName=ref(isEdit?row.name:'') |
|||
const newType=ref(isEdit?row.type:'') |
|||
|
|||
dynamicVNode.value=h(Chatbi_setting_panel,{ |
|||
title: isEdit?'编辑ChatBI实例':'新建ChatBI实例', |
|||
name:newName.value, |
|||
dbtype:newType.value, |
|||
checked:row?row.dataset:"", |
|||
confirmFunc:(_name:string,_type:string, data:string[])=>{ |
|||
if(_name!=""){ |
|||
const params={ |
|||
name: _name, |
|||
type: _type, |
|||
dataset: data.join(",") |
|||
} |
|||
const apiCall=isEdit?editChatBI({...params,uuid:row.uuid}):newChatBI(params) |
|||
|
|||
apiCall.then(()=>{ |
|||
ElMessage.success(isEdit?'编辑成功':'新建成功') |
|||
getChatBIList().then((res:any)=>{ |
|||
dataTable.value=res.data||[] |
|||
}) |
|||
}).catch(()=>{ |
|||
ElMessage.error(isEdit?'编辑失败':'新建失败') |
|||
}) |
|||
}else{ |
|||
ElMessage.warning('请填写完整信息') |
|||
} |
|||
dynamicVNode.value=null |
|||
}, |
|||
closeFunc:()=>dynamicVNode.value=null |
|||
}) |
|||
} |
|||
|
|||
const onChatBINew=()=>onChatBIEditOrNew(null) |
|||
const onEditChatBI=(row:any)=>onChatBIEditOrNew(row) |
|||
|
|||
const onDeleteChatBI=(row:any)=>{ |
|||
ElMessageBox.confirm(`确认删除项目( ${row.name}) ?删除后不可恢复!取消则放弃删除操作。`, "警告", { |
|||
confirmButtonText: "确定", |
|||
cancelButtonText: "取消", |
|||
type: "warning", |
|||
}).then(()=>{ |
|||
delChatBI({ |
|||
uuid: row.uuid |
|||
}).then(()=>{ |
|||
ElMessage.success('删除成功') |
|||
getChatBIList().then((res:any)=>{ |
|||
dataTable.value=res.data||[] |
|||
}) |
|||
}).catch(()=>{ |
|||
ElMessage.error('删除失败') |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
const askChatBI=(row:any)=>{ |
|||
dynamicVNode.value = h(ask_panel, { |
|||
'agent': { model: true, name: row.name, uuid: row.uuid,dbm:row.type,dataset:row.dataset }, |
|||
'closefunc': () => dynamicVNode.value = null |
|||
}) |
|||
} |
|||
|
|||
onMounted(()=>{ |
|||
getChatBIList().then((res:any)=>{ |
|||
dataTable.value=res.data||[] |
|||
}) |
|||
}) |
|||
|
|||
</script> |
|||
|
|||
<template> |
|||
<div> |
|||
<el-button @click="onChatBISetting">chatbi 设置</el-button> |
|||
<el-button @click="onChatBINew">chatbi 新建</el-button> |
|||
</div> |
|||
<div> |
|||
<el-table |
|||
stripe |
|||
:data="dataTable" |
|||
ref="multipleTableRef" |
|||
:header-cell-style="{ background: '#f5f8fd' }" |
|||
style="width: 100%" |
|||
row-key="uuid" |
|||
:row-style ="() => ({ lineHeight: '36px' })" |
|||
@cell-mouse-enter="handleMouseEnter" |
|||
@selection-change=""> |
|||
|
|||
<el-table-column width="450" property="name" label="项目名"> |
|||
</el-table-column> |
|||
<el-table-column width="360" property="type" label="类型"> |
|||
<template #default="scope"> |
|||
<span v-if="scope.row.type=='tsd'">时序型</span> |
|||
<span v-else >关系型</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column width="360" label="配置"> |
|||
<template #default="scope"> |
|||
<div v-show="currentHoverRow === scope.row.uuid"> |
|||
<span> |
|||
<el-button size="small" :icon="Edit" circle @click="onEditChatBI(scope.row)"></el-button> |
|||
<el-button size="small" :icon="Delete" circle @click="onDeleteChatBI(scope.row)"></el-button> |
|||
<el-button size="small" circle @click="askChatBI(scope.row)">问</el-button> |
|||
</span> |
|||
</div> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</div> |
|||
|
|||
<div v-if="dynamicVNode"> |
|||
<component :is="dynamicVNode" /> |
|||
</div> |
|||
|
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
|
|||
</style> |
|||
|
|||
<style> |
|||
.chatbi-new-dialog { |
|||
width: 800px !important; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,145 @@ |
|||
<!-- |
|||
@ 时间: 2025-12-30 |
|||
@ 备注: chatbi_setting_panel |
|||
--> |
|||
<script setup lang="ts"> |
|||
import { getOrPostDate } from "@/api/date/apidate"; |
|||
import { |
|||
dataSourceTypes, |
|||
interfaceTypes, |
|||
} from "@/api/date/type"; |
|||
|
|||
const props = withDefaults(defineProps<{ |
|||
title:string; |
|||
name:string; |
|||
dbtype:string; |
|||
checked:string, //当前勾选的数据库id |
|||
confirmFunc:(name:string,type:string, data:string[])=>void, //保存数据的回调, data是当前勾选的数据库id |
|||
closeFunc:()=>void, //父级只需销毁组件 |
|||
}>(),{}) |
|||
|
|||
const checkList = ref<string[]>([]) //选择的数据库id |
|||
const checkoptions=ref<{id:string,name:string,code:string}[]>([]) //数据库check option的数据源 |
|||
const newType=ref("") |
|||
const newName=ref("") |
|||
const opTotal=ref(0) |
|||
const dblist=ref<string[]>([]) //原有选择的数据库 |
|||
|
|||
watch(newType,(val)=>{ |
|||
if(val=="rdb"){ |
|||
onLoadData(1) |
|||
} |
|||
}) |
|||
|
|||
const onSaveChange=()=>{ |
|||
if(newType.value=="tsd") checkList.value=[] |
|||
props.confirmFunc(newName.value,newType.value, checkList.value) |
|||
} |
|||
|
|||
const onLoadData=(page:number)=>{ |
|||
freshPage(page) |
|||
} |
|||
|
|||
const freshPage=(page:number)=>{ |
|||
//加载数据库源 |
|||
let sendData = { |
|||
url: import.meta.env.VITE_APP_SJZT_URL + "/database/app/datasource/page", |
|||
methodType: "GET", |
|||
where: "pageNum="+page+"&pageSize=10&dataType=1" |
|||
}; |
|||
getOrPostDate("POST", sendData).then((data: any) => { |
|||
checkoptions.value=[] |
|||
opTotal.value=data.data.total; |
|||
if (data.data.records && data.data.records.length > 0) { |
|||
data.data.records.forEach((item: any) => { |
|||
dataSourceTypes.forEach((itemsd: any) => { |
|||
if (itemsd.value == item.datasourceType) { |
|||
item.datasourceTypeName = itemsd.label; |
|||
} |
|||
}); |
|||
interfaceTypes.forEach((interItem: any) => { |
|||
if (interItem.value == item.interfaceType) { |
|||
item.interfaceTypeName = interItem.label; |
|||
} |
|||
}); |
|||
|
|||
checkoptions.value.push({ |
|||
id:item.databaseName, |
|||
name:item.databaseName+item.dataSourceDes, |
|||
code:item.databaseName}) |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
onMounted(() => { |
|||
newName.value=props.name |
|||
newType.value=props.dbtype |
|||
if(props.checked!=""){ |
|||
checkList.value=props.checked.split(",") |
|||
dblist.value=props.checked.split(",") |
|||
} |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<el-dialog :model-value="true" :style="{'max-height': '880px'}" @close="closeFunc"> |
|||
<template #header> |
|||
<span>{{props.title}}</span> |
|||
</template> |
|||
<div class="tablelist"> |
|||
<el-input style="width:40%;margin:10px;" placeholder="请输入项目名称" v-model="newName"></el-input> |
|||
<el-select v-model="newType" style="width:40%;margin:10px;" placeholder="请选择数据库类型" valueKey="value"> |
|||
<el-option key="rdb" value="rdb">关系型</el-option> |
|||
<el-option key="tsd" value="tsd">时序型</el-option> |
|||
</el-select> |
|||
|
|||
<div v-if="newType=='rdb'" style="margin: 10px;"> |
|||
<div style="width: 100%;" >当前关联数据库: |
|||
<span class="dbtag" v-for="value in dblist">{{ value }}</span> |
|||
</div> |
|||
<el-checkbox-group v-model="checkList" > |
|||
<el-checkbox v-for="op in checkoptions" :id="op.id" :label="op.name" :value="op.code" style="width: 40%;"/> |
|||
</el-checkbox-group> |
|||
<div v-if="opTotal>10" style="margin-top: 16px;"> |
|||
<el-pagination size="small" @current-change="onLoadData" background layout="prev, pager, next" :total="opTotal" :page-size="10"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<template #footer> |
|||
<div class="dialog-footer"> |
|||
<el-button @click="closeFunc()">取消</el-button> |
|||
<el-button type="primary" @click="onSaveChange">保存</el-button> |
|||
</div> |
|||
</template> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
.menus_tree{ |
|||
display: block; |
|||
padding: 2px; |
|||
margin-right: 24px; |
|||
box-shadow: 0px 0px 12px rgb(0 0 0 / 18%); |
|||
} |
|||
.tablelist{ |
|||
margin-bottom:20px; |
|||
display:flex; |
|||
flex-direction:column; |
|||
width: 90%; |
|||
} |
|||
|
|||
.checkTitle{ |
|||
font-weight: bold; |
|||
} |
|||
.dbtag{ |
|||
background-color: #c3c1bd; |
|||
padding: 1px 9px; |
|||
border-radius: 7px; |
|||
margin: 0 4px; |
|||
} |
|||
</style> |
|||
|
|||
|
|||
|
|||
@ -0,0 +1,352 @@ |
|||
<!-- |
|||
@ 作者: han2015 |
|||
@ 时间: 2025-05-12 15:39:13 |
|||
@ 备注: chatbi ask_panel |
|||
--> |
|||
<script lang="ts" setup> |
|||
|
|||
import {ElText,ElInput} from "element-plus"; |
|||
import {Promotion,Remove } from '@element-plus/icons-vue' |
|||
import { getOrPostDate } from "@/api/date/apidate"; |
|||
import {askChatBI} from "@/api/date/chatbi" |
|||
import * as echarts from 'echarts/core'; |
|||
import { nextTick } from 'vue' |
|||
|
|||
const props = withDefaults(defineProps<{ |
|||
closefunc:()=>void, |
|||
agent:{model:boolean,name:string,uuid:string,dbm:string,dataset:string} |
|||
}>(),{}) |
|||
|
|||
interface charData{ |
|||
dataList:Object[]; |
|||
dimCols:string[]; |
|||
measureCols:string[]; |
|||
} |
|||
|
|||
const myquestion=ref('') |
|||
const currentAgent=ref<{name:string,uuid:string,model:boolean,dbm:string}>({}) |
|||
const interact_msg=ref<{ask:boolean,think:string,content:string,chart?:charData}[]>([]) |
|||
const inputState=ref(true) |
|||
let relationDB={} |
|||
|
|||
//消息体 |
|||
interface message{ |
|||
ask:boolean, |
|||
think:string, |
|||
content:string |
|||
} |
|||
|
|||
async function onSendTextToAI(){ |
|||
interact_msg.value.push({ask:true,think:"",content:myquestion.value}) |
|||
const params={"user_query":myquestion.value} |
|||
if(props.agent.dbm=="rdb"){ |
|||
switch(relationDB.datasourceType){ |
|||
case "4": |
|||
params.db_type="sqlserver" |
|||
break; |
|||
case "8": |
|||
params.db_type="postgresql" |
|||
break; |
|||
default: |
|||
params.db_type="mysql" |
|||
} |
|||
params.db_config={ |
|||
host:relationDB.ipAddress, |
|||
port:relationDB.port, |
|||
database:relationDB.databaseName, |
|||
username:relationDB.account, |
|||
password:relationDB.password, |
|||
} |
|||
} |
|||
myquestion.value="" |
|||
askChatBI(params,props.agent.dbm).then((res:any)=>{ |
|||
interact_msg.value.push({ask:false,think:"",content:"",chart:res.data}) |
|||
}).catch((err:any)=>{ |
|||
ElMessage.error("请求异常:"+err.message) |
|||
}) |
|||
} |
|||
|
|||
const drawChart= async (name:string,data:Object)=>{ |
|||
const option= buildSmoothLineOption(data.chart) |
|||
await nextTick() |
|||
|
|||
const dom=document.getElementById(name) |
|||
if(dom){ |
|||
const myChart = echarts.init(dom); |
|||
myChart.setOption(option); |
|||
} |
|||
} |
|||
|
|||
// 通用转换函数 |
|||
const buildSmoothLineOption=(chart:any)=>{ |
|||
const dimKey = chart.dimCols[0]; // "时间" |
|||
const measureKey = chart.measureCols[0]; // "预塔塔顶压力" |
|||
|
|||
const xAxisData = chart.dataList.map(item => item[dimKey]); |
|||
const seriesData = chart.dataList.map(item => Number(item[measureKey])); |
|||
|
|||
const option= { |
|||
tooltip: {}, |
|||
grid:{ |
|||
left: 10, |
|||
right: 10, |
|||
bottom: 0, |
|||
top: 30, |
|||
containLabel: true |
|||
}, |
|||
xAxis: { |
|||
type: "category", |
|||
boundaryGap: true, |
|||
axisLabel: { |
|||
rotate: 45, // 设置标签旋转45度 |
|||
color: '#333', |
|||
fontSize: 12, |
|||
}, |
|||
data: xAxisData |
|||
}, |
|||
yAxis: { |
|||
type: "value", |
|||
}, |
|||
series: [{ |
|||
name: measureKey, |
|||
type: "line", |
|||
smooth: true, |
|||
data: seriesData, |
|||
lineStyle: { width: 2 }, |
|||
itemStyle: { color: "#5470c6" } |
|||
}], |
|||
dataZoom:null, |
|||
}; |
|||
|
|||
if (seriesData.length>100){ |
|||
option.dataZoom=[ |
|||
{ |
|||
type: 'slider', // 滑块型滚动条 |
|||
xAxisIndex: 0, // 只控制第 0 条 x 轴 |
|||
show: true, |
|||
realtime: true, // 拖拽时实时刷新 |
|||
start: 0, // 起始百分比 |
|||
end: 30, // 结束百分比(可视范围) |
|||
height: 5, // 滚动条本身高度 |
|||
bottom: 5, // 距离容器底部 |
|||
handleSize: '40%', |
|||
// handleStyle: { color: 'rgba(27,90,169,1)' }, |
|||
// backgroundColor: 'rgba(37,46,100,.8)', |
|||
// fillerColor: 'rgba(27,90,169,1)', |
|||
borderColor: 'transparent' |
|||
} |
|||
// // 可选:再配一个内置型(鼠标滚轮/触摸板缩放) |
|||
// { type: 'inside', xAxisIndex: 0 } |
|||
] |
|||
} |
|||
|
|||
return option; |
|||
} |
|||
|
|||
//加载当前chatbi实例绑定的数据库 |
|||
const getRefDatabase=(name:string)=>{ |
|||
//加载数据库源 |
|||
let sendData = { |
|||
url: import.meta.env.VITE_APP_SJZT_URL + "/database/app/datasource/page", |
|||
methodType: "GET", |
|||
where: `pageNum=1&databaseName=${name}&pageSize=10&dataType=1` |
|||
}; |
|||
getOrPostDate("POST", sendData).then((res: any) => { |
|||
if(res.data.records.length>0){ |
|||
relationDB=res.data.records[0] |
|||
} |
|||
}); |
|||
} |
|||
|
|||
//渲染完页面再执行 |
|||
onMounted(() => { |
|||
currentAgent.value=props.agent |
|||
|
|||
getRefDatabase(props.agent.dataset) |
|||
|
|||
// interact_msg.value.push({ask:true,think:"",content:"预塔塔顶压力,最近一周的数据趋势"}) |
|||
// interact_msg.value.push({ask:false,think:"",content:"", chart:{ |
|||
// chart: { |
|||
// "dataList": [ |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:47", |
|||
// "name": "【2025】21期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 278271, |
|||
// "uuid": "fdae94f6-7fb8-491e-b075-b98595269580" |
|||
// }, |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:46", |
|||
// "name": "【2025】18期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 287256, |
|||
// "uuid": "ce40f7b1-d84b-4709-adf8-4dfc83cf2882" |
|||
// }, |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:46", |
|||
// "name": "【2025】20期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 246410, |
|||
// "uuid": "6e58feb4-f4e8-4eaf-9242-1ce0735e106b" |
|||
// }, |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:46", |
|||
// "name": "【2025】19期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 306184, |
|||
// "uuid": "c8a14d7b-336f-4ccf-8fda-39ff9aafd7e0" |
|||
// }, |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:45", |
|||
// "name": "【2025】15期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 212694, |
|||
// "uuid": "8ef700cc-a121-4cdc-87d7-ec9f090083e2" |
|||
// }, |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:45", |
|||
// "name": "【2025】17期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 227187, |
|||
// "uuid": "0eb8c280-9f13-40d3-bef0-ab88946d38e5" |
|||
// }, |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:45", |
|||
// "name": "【2025】16期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 236041, |
|||
// "uuid": "f97bd81b-82ad-48f0-8666-3f02f8c851de" |
|||
// }, |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:44", |
|||
// "name": "【2025】12期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 219716, |
|||
// "uuid": "9b748cc9-4def-49d0-baa8-567b6bf50673" |
|||
// }, |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:44", |
|||
// "name": "【2025】14期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 203773, |
|||
// "uuid": "70b045db-766c-45ca-a54a-be8df2adc396" |
|||
// }, |
|||
// { |
|||
// "create_time": "2026-01-09T08:43:44", |
|||
// "name": "【2025】13期总经理办公会暨周大调度会会议纪要.pdf", |
|||
// "size": 218646, |
|||
// "uuid": "da728907-b787-4d36-b210-820bd2632dac" |
|||
// } |
|||
// ], |
|||
// "dimCols": [ |
|||
// "create_time", |
|||
// "name", |
|||
// "uuid" |
|||
// ], |
|||
// "measureCols": [ |
|||
// "size" |
|||
// ] |
|||
// }, |
|||
// message: "已为您生成从matter表查找最新10个文件的SQL语句,包含创建时间、名称、uuid和大小信息,按创建时间降序排列。", |
|||
// sql: "SELECT DISTINCT create_time, name, uuid, size FROM tank31_matter ORDER BY create_time DESC LIMIT 10" |
|||
// }}) |
|||
|
|||
}); |
|||
</script> |
|||
|
|||
<template> |
|||
<el-drawer |
|||
:model-value="currentAgent.model" |
|||
:title="currentAgent.name+' : 问数'" |
|||
direction="rtl" |
|||
size="60%" |
|||
@close="props.closefunc()" |
|||
:style="{padding:'17px',backgroundColor:'#f3f3f3'}"> |
|||
<div style="position: relative;background: white;height:85%;overflow-y: auto;"> |
|||
<div class="reply_area" > |
|||
<template v-for="msg,index of interact_msg"> |
|||
<el-text v-if="msg.ask" class="t_ask" >{{ msg.content }}</el-text> |
|||
<div v-else class="t_resp"> |
|||
<el-text >{{ msg.chart?.message }}</el-text> |
|||
<div v-if="props.agent.dbm=='tsd'"> |
|||
<div :id="'chart_'+index" style="width: 100%;height:360px;"> |
|||
{{ drawChart('chart_'+index,msg.chart!) }} |
|||
</div> |
|||
</div> |
|||
<div v-else> |
|||
<el-text v-if="msg.chart?.sql" class="sql">{{ msg.chart?.sql }}</el-text> |
|||
<div :id="'chart_'+index" style="width: 100%;height:360px;overflow: scroll;"> |
|||
<el-table :data="msg.chart?.chart.dataList"> |
|||
<el-table-column v-for="value in msg.chart?.chart.dimCols" :prop="value" :label="value"></el-table-column> |
|||
<el-table-column v-for="value in msg.chart?.chart.measureCols" :prop="value" :label="value"></el-table-column> |
|||
</el-table> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</template> |
|||
</div> |
|||
<div class="question_com" :class="{newquestion:interact_msg.length>0}"> |
|||
<h1 v-show="interact_msg.length==0" style="font-size: 33px;font-weight: bold;margin: 10px;">恒信高科ChatBI服务</h1> |
|||
<el-input placeholder="问..." v-model="myquestion" input-style="border-radius: 20px;" |
|||
resize="none" :autosize="{minRows: 4}" type="textarea" /> |
|||
<el-button :style="{display :inputState ? '':'none'}" type="primary" :icon="Promotion" circle @click="onSendTextToAI"/> |
|||
<el-button :style="{display :inputState ? 'none':''}" type="primary" :icon="Remove" circle @click=""/> |
|||
<span>内容由 AI 生成,请仔细甄别</span> |
|||
</div> |
|||
</div> |
|||
</el-drawer> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
.app_container { |
|||
padding: 10px 30px 0px 30px; |
|||
height: 100%; |
|||
width: 100%; |
|||
} |
|||
.newquestion{ |
|||
bottom: 20px; |
|||
} |
|||
.question_com{ |
|||
position: fixed; |
|||
width: 52%; |
|||
margin: 0 50px; |
|||
text-align: center; |
|||
display: block; |
|||
button{ |
|||
position: absolute; |
|||
bottom: 25px; |
|||
right: 5px; |
|||
} |
|||
} |
|||
.reply_area{ |
|||
display: flex; |
|||
min-height: 20%; |
|||
flex-direction: column; |
|||
margin: 15px 15px 110px 0px; |
|||
} |
|||
|
|||
.t_ask{ |
|||
align-self: end; |
|||
line-height: 34px; |
|||
background-color: rgb(188 211 241); |
|||
padding: 0 30px; |
|||
border-radius:10px; |
|||
margin: 15px; |
|||
} |
|||
.t_resp{ |
|||
align-self: start; |
|||
line-height: 30px; |
|||
margin: 20px 33px; |
|||
font-size: 16px; |
|||
color: black; |
|||
width: 92%; |
|||
.sql{ |
|||
display: block; |
|||
padding: 8px; |
|||
margin: 10px 0; |
|||
background: #e5e5e5; |
|||
color: #0a4de9; |
|||
border-radius: 8px; |
|||
white-space: pre-wrap; |
|||
word-break: break-all; |
|||
} |
|||
table { |
|||
width: 100%; |
|||
display: table; |
|||
border: 0px solid; |
|||
overflow: scroll; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,119 @@ |
|||
<!-- |
|||
@ 时间: 2025-12-30 |
|||
@ 备注: chatbi, 设备和设备测点选择组件 |
|||
--> |
|||
|
|||
<script setup lang="ts"> |
|||
import {getDevicesTree,getDevicesMonitors} from "@/api/date/chatbi" |
|||
|
|||
|
|||
const props = withDefaults(defineProps<{ |
|||
checked:string[], |
|||
confirmFunc:(data:string[])=>void, //父级组件完全接管提交流程,组件不在做相关处理 |
|||
closeFunc:()=>void, //父级只需销毁组件 |
|||
}>(),{}) |
|||
|
|||
const firstLevelKeys = ref<string[]>(["HXGK","JCFC"]) //默认展开的部门 |
|||
const treeData=ref([]) |
|||
const checkList = ref<string[]>([]) |
|||
const checkoptions=ref<{id:string,name:string,code:string}[]>([]) |
|||
const nodeName=ref("") |
|||
const opTotal=ref(0) |
|||
const currentNode=ref<{id:string}>({id:""}) |
|||
|
|||
|
|||
const onSaveChange=()=>{ |
|||
props.confirmFunc(checkList.value) |
|||
} |
|||
|
|||
const onSelectDevice=(node)=>{ |
|||
nodeName.value=node.label |
|||
currentNode.value=node |
|||
freshPage(1) |
|||
} |
|||
|
|||
const onLoadData=(page:number)=>{ |
|||
freshPage(page) |
|||
} |
|||
|
|||
const freshPage=(page:number)=>{ |
|||
getDevicesMonitors({ |
|||
deviceCode:currentNode.value?.id, |
|||
rows:30, |
|||
page:page |
|||
}).then(res=>{ |
|||
checkoptions.value=[] |
|||
opTotal.value=0 |
|||
if(res.status==200){ |
|||
opTotal.value=res.data.total |
|||
res.data.rows?.forEach(row => { |
|||
checkoptions.value.push({id:row.id,name:row.name,code:row.code}) |
|||
}); |
|||
}else{ |
|||
ElMessage.error('测点获取失败!') |
|||
} |
|||
}) |
|||
} |
|||
|
|||
onMounted(() => { |
|||
checkList.value=props.checked |
|||
getDevicesTree().then((res)=>{ |
|||
treeData.value = res.data |
|||
}) |
|||
}) |
|||
</script> |
|||
|
|||
<template> |
|||
<el-dialog :model-value="true" :style="{'max-height': '880px'}" @close="closeFunc"> |
|||
<template #header> |
|||
<span>请选择设备测点</span> |
|||
</template> |
|||
<div style="display: grid;width: 100%;grid-template-columns:1fr 2fr;"> |
|||
<div class="menus_tree"> |
|||
<el-tree |
|||
ref="treeRef" |
|||
:data="treeData" |
|||
node-key="id" |
|||
:check-on-click-leaf="false" |
|||
:style="{maxHeight:'500px','overflow-y': 'auto'}" |
|||
:props="{label: 'label',children:'children',isLeaf:'children'}" |
|||
:default-expanded-keys="firstLevelKeys" |
|||
@node-click="onSelectDevice" |
|||
/> |
|||
</div> |
|||
<div class="tablelist"> |
|||
<div class="checkTitle" v-if="checkoptions.length==0&&nodeName!=''">{{nodeName}}: 没有测点!</div> |
|||
<div class="checkTitle" v-else>当前节点:{{nodeName}} </div> |
|||
<el-checkbox-group v-model="checkList" > |
|||
<el-checkbox v-for="op in checkoptions" :id="op.id" :label="op.name" :value="op.code" /> |
|||
</el-checkbox-group> |
|||
<div v-if="opTotal>30" style="margin-top: 16px;"> |
|||
<el-pagination size="small" @current-change="onLoadData" background layout="prev, pager, next" :total="opTotal" :page-size="30"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<template #footer> |
|||
<div class="dialog-footer"> |
|||
<el-button @click="closeFunc()">取消</el-button> |
|||
<el-button type="primary" @click="onSaveChange">保存</el-button> |
|||
</div> |
|||
</template> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
.menus_tree{ |
|||
display: block; |
|||
padding: 2px; |
|||
margin-right: 24px; |
|||
box-shadow: 0px 0px 12px rgb(0 0 0 / 18%); |
|||
} |
|||
|
|||
.checkTitle{ |
|||
font-weight: bold; |
|||
} |
|||
</style> |
|||
|
|||
|
|||
|
|||
Loading…
Reference in new issue