Browse Source

fix:移动端添加AI预测模型

chenlei 5 days ago
parent
commit
f0311778ec

+ 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


+ 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: '暂无表格权限,如有问题请联系管理员!',

+ 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(['update'])
+
+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('update',arr)
+    },
+    {
+        immediate:true,
+        deep:true
+    }
+)
+</script>
+
+
+<template>
+    <div class="sandTableImg-insert-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>
+.sandTableImg-insert-wrap{
+    height: 100%;
+    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>

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

@@ -0,0 +1,205 @@
+<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(['update'])
+
+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,
+            UniqueCode:`isETAForumChart_${item.UniqueCode}`,//在获取图表详情时通过前缀区分
+            HaveOperaAuth:true
+          })
+        });
+        listState.finished=res.Data?.Paging.IsEnd
+    }
+}
+getChartList()
+// 触底加载更多
+function onLoadMore(){
+    if(listState.finished||listState.loading||!listState.Keyword) 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('update',selectChartList.value)
+    },
+    {
+        immediate: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: 100%;
+    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;
+            max-height: 336px;
+            .title{
+                font-size: 28px;
+                min-height: 60px;
+            }
+            img{
+                width: 100%;
+            }
+        }
+        .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{
+            
+            .chart-item{
+                width: 260px;
+                border-width: 1px;
+                border-radius: 2px;
+                margin-bottom: 15px;
+                padding: 7px;
+                max-height: 220px;
+                .title{
+                    font-size: 14px;
+                    min-height: 30px;
+                }
+            }
+            .active{
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+}
+</style>

+ 41 - 1
src/views/report/components/reportInsert/Index.vue

@@ -11,6 +11,8 @@ import StatisticAnalysis from './StatisticAnalysis.vue'
 import PriceChart from './PriceChart.vue'
 import BalanceSheet from './BalanceSheet/Index.vue'
 import ETAForumChart from './ETAForumChart.vue'
+import AIPredictModel from './AIPredictModel.vue'
+import AIPredictFrame from './AIPredictFrame.vue'
 
 const emits=defineEmits(['insert'])
 
@@ -56,6 +58,11 @@ const typeOpt=ref([
         label:'商品价格曲线',
         path:'commordityChartBase'
     },
+    {
+        value:'AI预测模型',
+        label:'AI预测模型',
+        path:'AIPredictEdb'
+    },
     {
         value:'沙盘插入',
         label:'逻辑图',
@@ -67,7 +74,20 @@ const typeOpt=ref([
         path:'semanticsPage'
     },
 ])
+const AiTypeOpt=ref([
+    {
+        value:'预测模型',
+        label:'预测模型图',
+        path:'AIPredictEdb'
+    },
+    {
+        value:'模型框架',
+        label:'模型框架',
+        path:'AIPredictEdb'
+    },
+])
 const activeType=ref('素材库')
+const activeTypeAI=ref('预测模型')
 async function initTypeOpts(){
     const res=await apiMenuList()
     if(res.Ret===200){
@@ -105,8 +125,15 @@ 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预测模型'){
+        if (activeTypeAI.value === '预测模型') {
+            let filterList = list.value;
+            emits('insert',{list: filterList,type:'iframe',chartType:'chart'})
+        } else if (activeTypeAI.value === '模型框架') {
+            emits('insert',{list:list.value,type:'img',chartType:''})    
+        }
     }
     
     list.value=[]
@@ -134,6 +161,19 @@ function handleConfirmInsert(){
             <PriceChart @update="handleSelectChart" v-if="activeType==='商品价格曲线'"/>
             <BalanceSheet @update="handleSelectChart" @insert="handleConfirmInsert" v-if="activeType==='平衡表'"/>
             <ETAForumChart @update="handleSelectChart" v-if="activeType==='etaForum'"/>
+
+            <div v-if="activeType==='AI预测模型'">
+                <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 @update="handleSelectChart" v-if="activeTypeAI==='预测模型'"/>
+                <AIPredictFrame @update="handleSelectChart" v-if="activeTypeAI==='模型框架'"/>
+            </div>
         </div>
         <div class="bot-btn" v-if="activeType!=='平衡表'">
             <van-button type="primary" block @click="handleConfirmInsert" :disabled="list.length===0">插入</van-button>