<template> <div class="shuJuQuShi flex" v-loading="echartLoading" element-loading-text="加载中..." element-loading-background="rgba(0, 0, 0,0.4)"> <div class="echarts" ref="echarts" v-show="!isShow"></div> <empty v-if="isShow" emptyText="暂未查到数据" :width="100" :height="100" style="margin-top: 50px"></empty> </div> <!-- 分析建议弹窗 --> <el-dialog v-model="dialogShow" title="AI生成分析建议" width="600px" append-to-body draggable> <el-form> <el-form-item label="请输入问题" prop="question"> <el-input type="textarea" v-model="question" style="width: 100%"></el-input> </el-form-item> </el-form> <el-button type="primary" @click="aiDataSuggest">开始生成</el-button> <div class="aiContent" v-loading="loadingAI" element-loading-text="AI正在分析中..."> <div class="suggestContDialog" v-html="suggestContent"></div> <img src="@/assets/newImgs/HMScreen/resultAI.gif" alt="" class="aiImg" /> </div> </el-dialog> </template> <script setup> import { graphicReport, aiToGetSuggest } from '@/api/sponeScreen/syntherticData.js'; import html2Canvas from 'html2canvas'; import { sysUpload } from '@/api/login.js'; const { proxy } = getCurrentInstance(); const propsColor = proxy.fixDict['propsColorYs']; // 根据因子的单位来匹配 const props = defineProps({ searchDate: Array, typicalLandInfo: Object, domSelect: Object, }); const question = ref('从排水专业分析这段数据'); const dialogShow = ref(false); const suggestContent = ref(''); const loadingAI = ref(false); const echartLoading = ref(true); const isShow = ref(false); const myChart = shallowRef(null); const yAxisData = ref([]); // 获取列表数据 function getPipelineEcharts() { echartLoading.value = true; let params = { startTime: proxy.moment(props.searchDate[0]).format('YYYY-MM-DD') + ' 08:00:00', endTime: proxy.moment(props.searchDate[1]).format('YYYY-MM-DD') + ' 08:00:00', stCode: props.typicalLandInfo.stCode, }; graphicReport(params).then(res => { let { data } = res; echartLoading.value = false; //initEcharts(data.propertyMonitorXList, data.propertyMonitorList) let targerts = ['小时水量', 'SS', '水位', '流速']; let newPropertyMonitorList = data['propertyMonitorList'] && data['propertyMonitorList'].filter(item => targerts.includes(item.monitorPropertyName)); data['propertyMonitorList'] = newPropertyMonitorList; getRainData(data); }); } //根据关联的雨量站获取雨量站数据 const getRainData = async val => { let params = { startTime: proxy.moment(props.searchDate[0]).format('YYYY-MM-DD') + ' 08:00:00', endTime: proxy.moment(props.searchDate[1]).format('YYYY-MM-DD') + ' 08:00:00', stCode: props.typicalLandInfo.referRainStCode, }; let { data } = await graphicReport(params); echartLoading.value = false; let rainData = {}; if (data.propertyMonitorList.length) { data.propertyMonitorList.forEach(item => { if (item.monitorPropertyName.includes('5分钟时段降水量')) { rainData = item; } }); } isShow.value = data.propertyMonitorXList.length ? false : true; if (val['propertyMonitorList'] && val['propertyMonitorList'].length) { val.propertyMonitorList.unshift(rainData); } else { val['propertyMonitorList'] = [rainData]; } initEcharts(data.propertyMonitorXList, val.propertyMonitorList); }; const initEcharts = (timeList, propertyMonitorList) => { if (!!myChart.value) { myChart.value.dispose(); } myChart.value = proxy.echarts.init(proxy.$refs.echarts); const xAxisData = propertyMonitorList.map((item, index) => { return { type: index == 0 ? 'category' : '', //x轴类目名称列表 data: timeList, //x 轴所在的 grid 的索引,默认0表示位于第一个 grid gridIndex: index, axisLabel: { color: '#2395FF', fontSize: 14, fontFamily: 'Source Han Sans CN', // show: index == propertyMonitorList.length - 1 ? true : false, }, axisLine: { show: true, onZero: false, lineStyle: { color: '#2395FF', width: 1, type: 'solid', }, show: true, }, }; }); yAxisData.value = propertyMonitorList.map((item, index) => { const current = propsColor.find(it => it.label === item.propertyUnit); var maxNum = Math.max(...item.ylist); return { type: 'value', //展示网格线 splitLine: { show: true, }, inverse: item.monitorPropertyName.includes('5分钟时段降水量') ? true : false, //y 轴所在的 grid 的索引,默认0表示位于第一个 grid gridIndex: index, name: `${item.monitorPropertyName}(${current.value})`, nameLocation: item.monitorPropertyName.includes('5分钟时段降水量') ? 'start' : 'end', nameGap: 10, //纵坐标名字与轴线之间的距离 nameTextStyle: { fontFamily: 'Source Han Sans CN', fontSize: '14px', color: current.color, align: 'left', // 设置文字左对齐 }, splitLine: { show: true, lineStyle: { type: 'dashed', color: '#ffffff1c', }, }, axisLine: { show: true, lineStyle: { color: '#2395FF', }, }, max: function (value) { if (current.value == 'mm') { return (Number(maxNum) * 1.1).toFixed(0); } }, axisLabel: { // formatter: '{value} ', color: '#47AFFE', fontSize: 14, fontFamily: 'Source Han Sans CN', align: 'right', // 文字右对齐 formatter: function (value, index) { if (value >= 10000 && value < 10000000) { value = value / 10000 + '万'; } else if (value >= 10000000) { value = value / 10000000 + '千万'; } if (index % 2 === 0) { return value; } else { return ''; } }, }, }; }); const seriesData = propertyMonitorList.map((item, index) => { const current = propsColor.find(it => it.label === item.propertyUnit); if (current.value == 'mm') { return { type: 'bar', name: `${item.monitorPropertyName}(${current.value})`, data: item.ylist, //y轴类目数据 barWidth: '2px', itemStyle: { color: current.color, }, xAxisIndex: index, //使用x轴的索引 yAxisIndex: index, //使用y轴的索引 }; } else { return { type: 'line', name: `${item.monitorPropertyName}(${current.value})`, data: item.ylist, //y轴类目数据 lineStyle: { color: current.color, // 设置线的颜色为红色 width: 2, }, xAxisIndex: index, //使用x轴的索引 yAxisIndex: index, //使用y轴的索引 }; } }); const legendData = propertyMonitorList.map((item, index) => { const current = propsColor.find(it => it.label === item.propertyUnit); let topVal = 10 + 130 * index; return { data: [`${item.monitorPropertyName}(${current.value})`], show: false, color: current.color, top: topVal, }; }); const gridData = propertyMonitorList.map((item, index) => { let topVal = 45 + 135 * index; let bottomVal = 730 - 133 * (index + 1); return { top: topVal, bottom: bottomVal, left: 50, right: 0, }; }); const dataZoomXAxisIndex = propertyMonitorList.map((item, index) => { return index; }); const option = { color: ['#23d0df4a'], axisPointer: { link: [ { xAxisIndex: 'all', }, ], }, legend: legendData, grid: gridData, tooltip: { trigger: 'axis', show: true, backgroundColor: 'rgba(18,56,104,0.9)', // 设置背景色为半透明的白色 borderColor: '#48ABFF', // 边框颜色 borderWidth: 1, // 边框宽度 borderRadius: 4, // 设置边框圆角 textStyle: { // 设置字体颜色 color: '#F0F8FF', fontFamily: 'Source Han Sans CN', fontWeight: 500, fontSize: '15px', color: '#F0F8FF', }, formatter: function (params, b, c) { let dataArr = []; let newDataArr = []; let formatterStr = ''; params.map(function (item, index) { dataArr[item.seriesIndex] = item; }); dataArr.map(function (item, index) { newDataArr.push({ seriesName: item.seriesName, name: item.name, value: item.value }); }); formatterStr += params[0].axisValue + '<br />'; newDataArr.map(function (item, index) { formatterStr += item.seriesName + ':' + item.value + '<br />'; }); return formatterStr; }, }, dataZoom: { // 设置滚动条的隐藏与显示 show: true, // 设置滚动条类型 type: 'inside', // 设置背景颜色 backgroundColor: '#F1F1F1', // 设置选中范围的填充颜色 fillerColor: '#C1C1C1', // 设置边框颜色 borderColor: '#F1F1F1', // 是否显示detail,即拖拽时候显示详细数值信息 showDetail: false, // 数据窗口范围的起始数值 start: 0, // 数据窗口范围的结束数值(一页显示多少条数据) end: 100, // empty:当前数据窗口外的数据,被设置为空。 // 即不会影响其他轴的数据范围 filterMode: 'empty', // 设置滚动条宽度,相对于盒子宽度 width: '50%', // 设置滚动条高度 height: 8, // 设置滚动条显示位置 left: 'center', // 是否锁定选择区域(或叫做数据窗口)的大小 zoomLoxk: true, // 控制手柄的尺寸 handleSize: 0, // dataZoom-slider组件离容器下侧的距离 bottom: 0, xAxisIndex: dataZoomXAxisIndex, }, xAxis: xAxisData, yAxis: yAxisData.value, series: seriesData, }; myChart.value.setOption(option); }; // 显示输入问题的弹窗 function showDialogAI() { dialogShow.value = true; } // 生成建议 function aiDataSuggest() { loadingAI.value = true; html2Canvas(props.domSelect, { dpi: 300, // 解决生产图片模糊 useCORS: true, allowTaint: false, logging: false, scale: 1, }).then(res => { downloadBase64(res.toDataURL(), 'aiDataSuggest'); }); } const downloadBase64 = (dataUrl, fileName) => { const parts = dataUrl.split(';base64,'); const contentType = parts[0].split(':')[1]; let strArr = contentType.split('/'); const raw = window.atob(parts[1]); const rawLength = raw.length; const uInt8Array = new Uint8Array(rawLength); for (let i = 0; i < rawLength; ++i) { uInt8Array[i] = raw.charCodeAt(i); } const blob = new Blob([uInt8Array], { type: contentType }); const newFilename = fileName + '.' + strArr[1]; const file = new File([blob], newFilename, { type: contentType }); let formData = new FormData(); formData.append('file', file); sysUpload(formData).then(res => { if (res.code == 200) { let datas = res.data; // 文件状态改为上传成功 console.log('----上传成功', datas.url); let params = { content: question.value, pictureUrl: datas.url, }; aiToGetSuggest(params).then(res => { loadingAI.value = false; if (!!res.data) { let datas = JSON.parse(res.data); suggestContent.value = datas.output.choices[0].message.content[0].text; suggestContent.value = suggestContent.value.replace(/\n/g, '<br />'); } else { suggestContent.value = '暂无分析建议'; } }); } else { proxy.$modal.msgError('上传失败'); loadingAI.value = false; } }); }; onMounted(() => {}); defineExpose({ getPipelineEcharts, showDialogAI, }); </script> <style lang="scss"> .aiContent { position: relative; height: 600px; margin-top: 10px; margin-bottom: 10px; .suggestContDialog { width: 100%; height: 100%; overflow: auto; color: #c6c6c6; padding: 20px; background: url('@/assets/newImgs/HMScreen/contentBgAI.png') no-repeat; background-size: 100% 100%; } .aiImg { position: absolute; right: -20px; bottom: 0px; z-index: 99; width: 100px; height: 100px; } } .shuJuQuShi { width: 100%; height: 730px; .echarts { width: 100%; height: 100%; } } </style>