소스 검색

Merge branch 'ch/ETA_2.5.2' of eta_front/eta_mobile_front into master

leichen 1 개월 전
부모
커밋
bb88bd009a

+ 13 - 1
src/api/etaForum.js

@@ -17,5 +17,17 @@ export default{
   // 获取用户在ETA社区中有权限的图表
   getChartList:params=>{
     return get('/eta_forum/chart_list',params)
-  }
+  },
+  // 获取用户在ETA社区中有权限的图表
+  getAIPredictChartList:params=>{
+    return get('/ai_predict_model/chart/search_by_es',params)
+  },
+  /**
+   * 框架列表
+   * @param {*} params 
+   * @returns 
+   */
+  getFrameList: params => {
+    return get('/ai_predict_model/framework/list',params)
+  },
 }

BIN
src/assets/imgs/chartETA/default_chart.png


BIN
src/assets/imgs/chartETA/default_sanbox.png


+ 20 - 0
src/hooks/chart/config.js

@@ -243,3 +243,23 @@ export const yearSelectOpt = [
 // 拥有相同配置的图表类型集合
 export const sameOptionType=[1,3,4,5,6]
 
+/* 图表年份筛选框 */
+export const yearSelector = [
+	{
+		name: '15年至最新值',//'15年至最新值'
+		value: 3,
+	},
+	{
+		name: '20年至最新值',//'20年至最新值'
+		value: 9,
+	},
+	{
+		name: '23年至最新值',//'23年至最新值'
+		value: 12,
+	},
+	{
+		name: '24年至最新值',//'24年至最新值'
+		value: 13,
+	},
+]
+

+ 233 - 1
src/hooks/chart/render.js

@@ -1,7 +1,7 @@
 // 图渲染逻辑模块
 
 import {onMounted,ref,nextTick,reactive} from 'vue'
-import {chartDefaultOpts,scatterXAxis,basicYAxis,basicXAxis,leadUnitEnMap,relevanceUnitEnMap,seasonOptions} from './config'
+import {chartDefaultOpts,scatterXAxis,basicYAxis,basicXAxis,leadUnitEnMap,relevanceUnitEnMap,seasonOptions, yearSelector} from './config'
 import Highcharts from 'highcharts/highstock';
 import HighchartsFormat from 'highcharts';
 import HighchartsMore from 'highcharts/highcharts-more';
@@ -51,6 +51,9 @@ const axisLimitState = reactive({//极值数据
 let useSelfLimit = false
 let isUseSelfLimit = ref(false)
 
+const year_select = yearSelector[0].value
+const select_date = '' //自定义时间段
+
 const screen = ref(document.body.clientWidth < 1200 ? 'phone' : 'pc');
 // webcomponent的EtaChart.ce.vue文件用到了这里的逻辑
 // 有修改了 chartRender,setLimitData 这俩方法的,需要重新编辑下webcomponent(npm run build.lib),然后替换掉report项目的对应文件eta_comp.js
@@ -108,6 +111,8 @@ export function chartRender({data,renderId,lang='zh',changeLangIsCheck,showChart
     chartData.value=data
    /*  useSelfLimit = ['/myETA/chartdetail','/chartETA/chartdetail'].includes(window.location.pathname) */
    useSelfLimit = shouldUseSelfLimit
+   console.log(data.ChartInfo.Source);
+   
     if([1,11,12].includes(data.ChartInfo.Source)){
         const chartSetMap = {
             1: setSplineOpt,
@@ -142,6 +147,12 @@ export function chartRender({data,renderId,lang='zh',changeLangIsCheck,showChart
         axisLimitState.xMax=data.ChartInfo.XMax?Number(data.ChartInfo.XMax):Number(data.DataResp.XMaxValue),
 
         chartOpt = setCrossVarietyChart(data)
+    }else if (data.ChartInfo.Source === 14 || data.ChartInfo.Source === 15) {
+        if (data.ChartInfo.Source === 14) {
+            chartOpt = setAIPredictChartDaily(data);
+        } else {
+            chartOpt = setAIPredictChart(data);
+        }
     }
 
     /* 
@@ -2715,7 +2726,228 @@ function setRadarChart({DataResp,EdbInfoList,ChartInfo}) {
     }
 }
 
+// AI预测模型日度预测图表
+function setAIPredictChartDaily({ DataResp, EdbInfoList, ChartInfo }) {
+    axisLimitState.leftIndex = -1;
+    axisLimitState.rightIndex = -1;
+    axisLimitState.rightTwoIndex = -1;
+
+    const chart = {
+        spacing: [2,10,2,10],
+        plotBorderColor: '#000',
+        plotBorderWidth: 1
+    };
+
+    const plotLines = [
+        {
+            color: '#000',
+            width: 1,
+            dashStyle: 'Dash',
+            label: {
+                text: 'Future Split',
+                verticalAlign: 'middle',
+                textAlign: 'center'
+            },
+            value: DataResp ? DataResp.ActualLatestTimestamp : 0
+        }
+    ];
+
+    const xAxis = {
+        tickPosition: 'inside',
+        lineColor: '#bfbfbf',
+        labels: {
+            formatter: function (ctx) {
+                return Highcharts.dateFormat('%Y-%m', ctx.value);
+            },
+            style: {}
+        },
+        lineColor: '#000',
+        tickWidth: 0,
+        tickColor: '#ccc',
+        gridLineColor: '#ccc',
+        gridLineWidth: 1,
+        plotLines: plotLines
+    };
+
+    const yAxis = {
+        ...basicYAxis,
+        title: {
+            enabled: false,
+            text: ChartInfo.Unit,
+            textCh: ChartInfo.Unit, // 中文
+            textEn: ChartInfo.UnitEn, // 英文
+            style: {},
+            align: 'high',
+            rotation: 0,
+            y: 12,
+            x: -20,
+            textAlign: 'left',
+            reserveSpace: false
+        },
+        labels: {
+            formatter: function (ctx) {
+                return ctx.value;
+            },
+            align: 'center',
+            x: -5,
+            y: 8,
+            style: {}
+        },
+        opposite: false,
+        min: Number(ChartInfo.LeftMin || 0),
+        max: Number(ChartInfo.LeftMax || 0),
+        tickWidth: 0,
+        tickColor: '#ccc',
+        lineColor: '#000',
+        gridLineColor: '#ccc',
+        gridLineWidth: 1
+    };
+
+    const data = [];
+    const chartData = _.cloneDeep(EdbInfoList) || [];
+    chartData.forEach((item, index) => {
+        const obj = {
+            data: [],
+            type: 'line',
+            dashStyle: index === 1 ? 'Dash' : 'Solid',
+            yAxis: 0,
+            name: item.EdbName,
+            nameCh: item.EdbName,
+            nameEn: item.EdbName,
+            color: item.ChartColor, // 控制线条颜色
+            lineWidth: Number(item.ChartWidth) || 1
+        };
+        item.DataList = item.DataList || [];
+        for (const i of item.DataList) {
+            obj.data.push([i.DataTimestamp, i.Value]);
+        }
+        data.push(obj);
+    });
+
+    const legend = {
+        enabled: true,
+        align: 'left', // 图例水平对齐到左侧
+        verticalAlign: 'top', // 图例垂直对齐到顶部
+        layout: 'vertical', // 图例水平布局
+        x: 45, // 距离图表左侧的偏移量
+        y: 5, // 距离图表顶部的偏移量
+        floating: true, // 使图例悬浮在图表之上
+        backgroundColor: 'rgba(255, 255, 255, 0.8)', // 图例背景(可选)
+        borderWidth: 1, // 图例边框宽度(可选)
+        itemStyle: {
+            color: '#000', // 图例文本颜色
+            fontSize: '10px' // 图例字体大小
+        }
+    };
+
+    return {
+        chart,
+        series: data,
+        yAxis: [yAxis],
+        xAxis,
+        legend
+    };
+}
+
+// AI预测模型
+function setAIPredictChart({ DataResp, EdbInfoList, ChartInfo }) {
+    axisLimitState.leftIndex = -1;
+    axisLimitState.rightIndex = -1;
+    axisLimitState.rightTwoIndex = -1;
 
+    const isLessThanOneYear = xLabelDealHandle();
+    const xAxis = {
+        tickPosition: 'inside',
+        lineColor: '#bfbfbf',
+        labels: {
+            formatter: function (ctx) {
+                return isLessThanOneYear
+                    ? Highcharts.dateFormat('%m/%d', ctx.value)
+                    : Highcharts.dateFormat('%y/%m', ctx.value);
+            },
+            style: {}
+        }
+    };
+
+    const yAxis = {
+        ...basicYAxis,
+        title: {
+            text: ChartInfo.Unit,
+            textCh: ChartInfo.Unit, // 中文
+            textEn: ChartInfo.UnitEn, // 英文
+            style: {},
+            align: 'high',
+            rotation: 0,
+            y: -12,
+            x: 0,
+            textAlign: 'left',
+            reserveSpace: false
+        },
+        labels: {
+            formatter: function (ctx) {
+                return ctx.value;
+            },
+            align: 'center',
+            x: -5,
+            style: {}
+        },
+        opposite: false,
+        min: Number(ChartInfo.LeftMin || 0),
+        max: Number(ChartInfo.LeftMax || 0),
+        tickWidth: 1
+    };
+
+    const data = [];
+    const chartData = _.cloneDeep(EdbInfoList) || [];
+    chartData.forEach((item, index) => {
+        const obj = {
+            data: [],
+            type: 'line',
+            dashStyle: index === 1 ? 'Dash' : 'Solid',
+            yAxis: 0,
+            name: item.EdbName,
+            nameCh: item.EdbName,
+            nameEn: item.EdbName,
+            color: item.ChartColor, // 控制线条颜色
+            lineWidth: Number(item.ChartWidth) || 1,
+            marker: index > 0 ? {
+                enabled: true,
+                symbol: 'circle',
+                fillColor: "#f00",
+                radius: 3
+            } : {}
+        };
+        item.DataList = item.DataList || [];
+        for (const i of item.DataList) {
+            obj.data.push([i.DataTimestamp, i.Value]);
+        }
+        data.push(obj);
+    });
+
+    return {
+        series: data,
+        yAxis: [yAxis],
+        xAxis
+    };
+}
+
+/* 查询范围为1年内 x轴显示为月/日 否则默认年/月 */
+function xLabelDealHandle() {
+    const end =
+        year_select === 5
+            ? select_date[1]
+            : year_select === 6
+            ? new Date()
+            : '';
+
+    const year_differ = moment(end).diff(moment(select_date[0]), 'years', true);
+    // console.log(year_differ)
+    if ([5, 6].includes(year_select) && year_differ <= 1) {
+        return true;
+    } else {
+        return false;
+    }
+}
 //获取RGBA的透明度
 function parseRgbaColor(color='rgba(51, 51, 51, 1)'){
     const arr = color.match(/(\d(\.\d+)?)+/g) || ['','','',1];

+ 6 - 3
src/hooks/useDownLoadFile.js

@@ -13,14 +13,17 @@ import { PDFDocument } from "pdf-lib";
  *  cancelDownload  取消下载
  * }
  */
+
 export function useDownLoadFile() {
   let progress = ref(0); //进度 0-1
   let fileSize = ref(0); //文件大小
-
-  const controller = new AbortController();
-
+  let controller = ''
   // 开始下载
   const startDownload = (url, filename) => {
+    if(controller){
+      controller = null
+    }
+    controller = new AbortController();
     axios({
       url: url,
       method: "get",

+ 4 - 0
src/hooks/useNoAuth.js

@@ -1,5 +1,7 @@
 import noAuthImg from '@/assets/imgs/dataEDB/lock_big.png'
 import noAuthIco from '@/assets/imgs/dataEDB/lock_black.png'
+import noImg from '@/assets/imgs/chartETA/default_chart.png'
+import noSanBox from '@/assets/imgs/chartETA/default_sanbox.png'
 
 //无数据权限 缺省图和提示
 export function useNoAuth() {
@@ -7,6 +9,8 @@ export function useNoAuth() {
   return {
     noAuthImg,
     noAuthIco,
+    noImg,
+    noSanBox,
     edb: '暂无指标权限,如有问题请联系管理员!',
     chart: '暂无图表权限,如有问题请联系管理员!',
     sheet: '暂无表格权限,如有问题请联系管理员!',

+ 2 - 0
src/hooks/useReportApprove.js

@@ -16,6 +16,8 @@ export function useReportApprove(){
         const {IsReportApprove='',ReportApproveType='',IsOpenChartExpired} = etaConfigInfo.value
         isApprove.value = IsReportApprove==='true'?true:false
         isOtherApprove.value = isApprove.value&&ReportApproveType==='other' 
+        console.log('IsOpenChartExpired',IsOpenChartExpired);
+        
         isLinkChartExpired.value = IsOpenChartExpired==='true'?true:false;
     }
     //检查分类是否存在审批流

+ 2 - 1
src/views/ppt/components/ChartWrap.vue

@@ -46,7 +46,8 @@ async function getChartInfo(){
         //初始化上下限
         isUseSelfLimit.value = true
         setLimitData(res.Data)
-
+        console.log(res.Data);
+        
         chartRender({
             data:res.Data,
             renderId:renderId.value,

+ 77 - 0
src/views/report/components/reportInsert/AIForecastingModel.vue

@@ -0,0 +1,77 @@
+<script setup>
+import AIPredictModel from './AIPredictModel.vue'
+import AIPredictFrame from './AIPredictFrame.vue'
+import {ref} from 'vue'
+
+const emits=defineEmits(['update'])
+
+const AiTypeOpt=ref([
+    {
+        value:'预测模型',
+        label:'预测模型图',
+        path:'AIPredictEdb'
+    },
+    {
+        value:'模型框架',
+        label:'模型框架',
+        path:'AIPredictEdb'
+    },
+])
+
+const activeTypeAI=ref('预测模型')
+
+function handleSelectChartAI(data){
+    if (activeTypeAI.value === '预测模型') {
+        emits('update', data, 'iframe')
+    } else if (activeTypeAI.value === '模型框架') {
+        emits('update', data, 'img')
+    }
+}
+</script>
+
+
+<template>
+    <ul class="top-type-box">
+        <li 
+            :class="['item',activeTypeAI===item.value&&'active']"
+            v-for="item in AiTypeOpt" 
+            :key="item"
+            @click="activeTypeAI=item.value;list=[]"
+        >{{item.label}}</li>
+    </ul>
+    <AIPredictModel @updateList="handleSelectChartAI" v-if="activeTypeAI==='预测模型'"/>
+    <AIPredictFrame @updateList="handleSelectChartAI" v-if="activeTypeAI==='模型框架'"/>
+</template>
+
+<style lang="scss" scoped>
+.top-type-box{
+    background-color: $page-bg-grey;
+    display: flex;
+    border-bottom: 1px solid $border-color;
+    overflow-x: auto;
+    &::-webkit-scrollbar{
+        display: none;
+    }
+    .item{
+        padding: 24px 0;
+        flex: 1;
+        min-width: 105PX;
+        text-align: center;
+        border-top-right-radius: var(--van-popup-round-radius);
+        border-top-left-radius: var(--van-popup-round-radius);
+        transition: all 0.3s;
+    }
+    .active{
+        background-color: #fff;
+        color: $theme-color;
+        font-weight: 600;
+    }
+}
+@media screen and (min-width:$media-width){
+    .top-type-box{
+        .item{
+            padding: 12px 0;
+        }
+    }
+}
+</style>

+ 183 - 0
src/views/report/components/reportInsert/AIPredictFrame.vue

@@ -0,0 +1,183 @@
+<script setup>
+import {setHightLightText} from '@/hooks/common'
+import {reactive, ref,watch} from 'vue'
+import apiETAForum from '@/api/etaForum'
+import { vInfiniteScroll } from '@vueuse/components'
+import { useNoAuth } from '@/hooks/useNoAuth'
+
+const emits=defineEmits(['updateList'])
+
+const searchVal=ref('')
+const listState=reactive({
+    page:1,
+    pageSize:20,
+    list:[],
+    finished:false,
+    loading:false
+})
+async function getSandBoxList(){
+    const params={
+        Keyword:searchVal.value,
+        CurrentIndex:listState.page,
+        PageSize:listState.pageSize
+    }
+    listState.loading=true
+    let res=await apiETAForum.getFrameList(params)
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data||[]
+        listState.list=[...listState.list,...arr]
+        // listState.finished=res.Data?.Paging.IsEnd
+    }
+}
+getSandBoxList()
+// 触底加载更多
+function onLoadMore(){
+    if(listState.finished||listState.loading) return
+    listState.page++
+    getSandBoxList()
+}
+function handleRefreshList(){
+    listState.list=[]
+    listState.page=1
+    listState.finished=false
+    getSandBoxList()
+}
+
+let selectChartList=ref([])
+function handleSelect(item){
+    const index=selectChartList.value.indexOf(item.AiPredictModelFrameworkId)
+    if(index!==-1){
+        selectChartList.value.splice(index,1)
+    }else{
+        selectChartList.value.push(item.AiPredictModelFrameworkId)
+    }
+    
+}
+
+watch(
+    ()=>selectChartList.value,
+    ()=>{
+        const arr=listState.list.filter(item=>selectChartList.value.includes(item.AiPredictModelFrameworkId)).map(e=>e.FrameworkImg)
+        emits('updateList',arr)
+    },
+    {
+        immediate:true,
+        deep:true
+    }
+)
+</script>
+
+
+<template>
+    <div class="ETA-chart-wrap">
+        <van-search v-model="searchVal" shape="round" placeholder="请输入框架名称" @search="handleRefreshList" @clear="handleRefreshList" />
+        <div class="content-box">
+            <div v-if="listState.list.length==0&&listState.finished">
+                <img class="list-empty-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/empty_img.png" alt="">
+                <p style="text-align:center;color:#999999;font-size:12px">暂无图表</p>
+            </div>
+            
+            <ul class="chart-list" v-infinite-scroll="[onLoadMore, { 'distance' : 10 }]">
+                <li 
+                    :class="['chart-item',selectChartList.includes(item.AiPredictModelFrameworkId)&&'active']" 
+                    v-for="item in listState.list" 
+                    :key="item.AiPredictModelFrameworkId"
+                    @click="handleSelect(item)"
+                >
+                    <div class="van-multi-ellipsis--l2 title" v-html="setHightLightText(item.FrameworkName,searchVal)"></div>
+                    <img :src="item.FrameworkImg ? item.FrameworkImg : useNoAuth().noSanBox" alt="">
+
+                    <svg v-if="selectChartList.includes(item.AiPredictModelFrameworkId)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+                        <path d="M14.5 28C22.232 28 28.5 21.732 28.5 14C28.5 6.26801 22.232 0 14.5 0C6.76801 0 0.5 6.26801 0.5 14C0.5 21.732 6.76801 28 14.5 28ZM7.5 14.413L8.913 13L12.5 16.586L20.085 9L21.5 10.415L12.5 19.414L7.5 14.413Z" fill="#0052D9"/>
+                    </svg>
+
+                </li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+            </ul>
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.ETA-chart-wrap{
+    height: calc(100% - 100px);
+    display: flex;
+    flex-direction: column;
+    .content-box{
+        overflow: hidden;
+        flex: 1;
+        padding: var(--van-padding-sm);
+        min-height: 300PX;
+    }
+    .chart-list{
+        height: 100%;
+        overflow-y: auto;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: space-between;
+        .chart-item{
+            width: 48%;
+            background: #FFFFFF;
+            border: 3px solid $border-color;
+            box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.03);
+            border-radius: 4px;
+            margin-bottom: 30px;
+            overflow: hidden;
+            padding: 14px;
+            box-sizing: border-box;
+            position: relative;
+            .title{
+                font-size: 28px;
+                min-height: 60px;
+            }
+            img{
+                width: 100%;
+                height: 220px;
+                object-fit: contain;
+            }
+        }
+        .active{
+            border-color: $theme-color;
+            svg{
+                width: 28px;
+                height: 28px;
+                position: absolute;
+                right: 22px;
+                bottom: 22px;
+            }
+        }
+    }
+}
+@media screen and (min-width:$media-width){
+    .sandTableImg-insert-wrap{
+        .chart-list{
+            height: auto;
+            max-height: 100%;
+            .chart-item{
+                width: 260px;
+                border-width: 1px;
+                border-radius: 2px;
+                margin-bottom: 15px;
+                padding: 7px;
+                .title{
+                    font-size: 14px;
+                    min-height: 30px;
+                }
+                img{
+                    height: 150px;
+                }
+            }
+            .active{
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+}
+</style>

+ 209 - 0
src/views/report/components/reportInsert/AIPredictModel.vue

@@ -0,0 +1,209 @@
+<script setup>
+import {setHightLightText} from '@/hooks/common'
+import {reactive, ref, watch} from 'vue'
+import apiETAForum from '@/api/etaForum'
+import { showToast } from 'vant'
+import { vInfiniteScroll } from '@vueuse/components'
+import { useNoAuth } from '@/hooks/useNoAuth'
+
+const emits=defineEmits(['updateList'])
+
+const searchVal=ref('')
+
+const listState=reactive({
+    page:1,
+    pageSize:20,
+    list:[],
+    finished:false,
+    loading:false
+})
+
+async function getChartList(){
+    const params={
+        Keyword:searchVal.value,
+        CurrentIndex:listState.page,
+        PageSize:listState.pageSize,
+    }
+    listState.loading=true
+    
+    let res = await apiETAForum.getAIPredictChartList(params)
+    
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data.List||[]
+        arr.forEach(item => {
+          listState.list.push({
+            ...item,
+            HaveOperaAuth:true
+          })
+        });
+        listState.finished=res.Data?.Paging.IsEnd
+    }
+}
+getChartList()
+// 触底加载更多
+function onLoadMore(){
+    if(listState.finished||listState.loading) return
+    listState.page++
+    getChartList()
+}
+
+function handleRefreshList(){
+    listState.list=[]
+    listState.page=1
+    listState.finished=false
+    selectChartList.value=[]
+    getChartList()
+}
+
+const selectChartList=ref([])
+function handleSelect(item){
+    const index=selectChartList.value.indexOf(item.UniqueCode)
+    if(index!==-1){
+        selectChartList.value.splice(index,1)
+    }else{
+        selectChartList.value.push(item.UniqueCode)
+    }
+    
+}
+
+watch(
+    ()=>selectChartList.value,
+    ()=>{
+        emits('updateList',selectChartList.value)
+    },
+    {
+        immediate:true,
+        deep:true
+    }
+)
+
+</script>
+
+<template>
+    <div class="ETA-chart-wrap">
+        <div class="sticky-box">
+            <van-search v-model="searchVal" shape="round" placeholder="请输入预测模型图名称" @search="handleRefreshList" @clear="handleRefreshList" />
+        </div>
+        <div class="content-box">
+            <div v-if="listState.list.length==0&&listState.finished">
+                <img class="list-empty-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/empty_img.png" alt="">
+                <p style="text-align:center;color:#999999;font-size:12px">暂无图表</p>
+            </div>
+            
+            <ul class="chart-list" v-infinite-scroll="[onLoadMore, { 'distance' : 10 }]">
+                <li 
+                    :class="['chart-item',selectChartList.includes(item.UniqueCode)&&'active']" 
+                    v-for="item in listState.list" 
+                    :key="item.ChartInfoId"
+                    @click="handleSelect(item)"
+                >
+                    <div class="van-multi-ellipsis--l2 title" v-html="setHightLightText(item.ChartName,searchVal)"></div>
+                    <img style="max-height: 120px;" :src="item.ChartImage ? item.ChartImage : useNoAuth().noImg" alt="">
+
+                    <svg v-if="selectChartList.includes(item.UniqueCode)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+                        <path d="M14.5 28C22.232 28 28.5 21.732 28.5 14C28.5 6.26801 22.232 0 14.5 0C6.76801 0 0.5 6.26801 0.5 14C0.5 21.732 6.76801 28 14.5 28ZM7.5 14.413L8.913 13L12.5 16.586L20.085 9L21.5 10.415L12.5 19.414L7.5 14.413Z" fill="#0052D9"/>
+                    </svg>
+
+                </li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+            </ul>
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.ETA-chart-wrap{
+    height: calc(100% - 100px);
+    display: flex;
+    flex-direction: column;
+    .content-box{
+        overflow: hidden;
+        flex: 1;
+        padding: var(--van-padding-sm);
+        min-height: 300PX;
+    }
+    .chart-list{
+        height: 100%;
+        overflow-y: auto;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: space-between;
+        .chart-item{
+            width: 48%;
+            background: #FFFFFF;
+            border: 3px solid $border-color;
+            box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.03);
+            border-radius: 4px;
+            margin-bottom: 30px;
+            overflow: hidden;
+            padding: 14px;
+            box-sizing: border-box;
+            position: relative;
+            .title{
+                font-size: 28px;
+                min-height: 60px;
+            }
+            img{
+                width: 100%;
+                height: 220px;
+                object-fit: contain;
+            }
+        }
+        .active{
+            border-color: $theme-color;
+            svg{
+                width: 28px;
+                height: 28px;
+                position: absolute;
+                right: 22px;
+                bottom: 22px;
+            }
+        }
+    }
+}
+.sticky-box{
+    flex-shrink: 0;
+    // position: sticky;
+    // top: 0;
+    // z-index: 10;
+    background-color: #fff;
+    .van-tabs{
+        border-bottom: 1px solid $border-color;
+    }
+    .van-checkbox{
+        margin: var(--van-padding-sm);
+    }
+}
+@media screen and (min-width:$media-width){
+    .ETA-chart-wrap{
+        .chart-list{
+            height: auto;
+            max-height: 100%;
+            .chart-item{
+                width: 260px;
+                border-width: 1px;
+                border-radius: 2px;
+                margin-bottom: 15px;
+                padding: 7px;
+                .title{
+                    font-size: 14px;
+                    min-height: 30px;
+                }
+                img{
+                    height: 150px;
+                }
+            }
+            .active{
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+}
+</style>

+ 14 - 3
src/views/report/components/reportInsert/Index.vue

@@ -11,6 +11,7 @@ import StatisticAnalysis from './StatisticAnalysis.vue'
 import PriceChart from './PriceChart.vue'
 import BalanceSheet from './BalanceSheet/Index.vue'
 import ETAForumChart from './ETAForumChart.vue'
+import AIForecastingModel from './AIForecastingModel.vue'
 
 const emits=defineEmits(['insert'])
 
@@ -56,6 +57,11 @@ const typeOpt=ref([
         label:'商品价格曲线',
         path:'commordityChartBase'
     },
+    {
+        value:'AI预测模型',
+        label:'AI预测模型',
+        path:'AIPredictEdb'
+    },
     {
         value:'沙盘插入',
         label:'逻辑图',
@@ -67,6 +73,7 @@ const typeOpt=ref([
         path:'semanticsPage'
     },
 ])
+
 const activeType=ref('素材库')
 async function initTypeOpts(){
     const res=await apiMenuList()
@@ -92,9 +99,10 @@ initTypeOpts()
 
 
 let list=ref([])
-
-function handleSelectChart(data){
+const AIChartType = ref('iframe')
+function handleSelectChart(data,type){
     list.value=data
+    AIChartType.value=type
 }
 
 function handleConfirmInsert(){
@@ -105,8 +113,10 @@ function handleConfirmInsert(){
         emits('insert',{list: filterList,type:'iframe',chartType:'chart'})
     }else if(['平衡表','表格插入'].includes(activeType.value)){
         emits('insert',{list:list.value,type:'iframe',chartType:'sheet'})
-    }else if(['沙盘插入','语义分析插入','素材库'].includes(activeType.value)){
+    }else if(['沙盘插入','语义分析插入','素材库', ''].includes(activeType.value)){
         emits('insert',{list:list.value,type:'img',chartType:''})
+    } else if(activeType.value==='AI预测模型'){
+        emits('insert',{list:list.value, type:AIChartType.value, chartType: AIChartType.value === 'img' ? '' : 'chart'})
     }
     
     list.value=[]
@@ -134,6 +144,7 @@ function handleConfirmInsert(){
             <PriceChart @update="handleSelectChart" v-if="activeType==='商品价格曲线'"/>
             <BalanceSheet @update="handleSelectChart" @insert="handleConfirmInsert" v-if="activeType==='平衡表'"/>
             <ETAForumChart @update="handleSelectChart" v-if="activeType==='etaForum'"/>
+            <AIForecastingModel @update="handleSelectChart" v-if="activeType==='AI预测模型'"/>
         </div>
         <div class="bot-btn" v-if="activeType!=='平衡表'">
             <van-button type="primary" block @click="handleConfirmInsert" :disabled="list.length===0">插入</van-button>