Bladeren bron

素材库

yujinwen 1 maand geleden
bovenliggende
commit
347516607e

+ 1 - 1
package.json

@@ -38,7 +38,7 @@
     "v3-color-picker-teleport": "^1.0.9",
     "vant": "^4.6.4",
     "vconsole": "^3.15.0",
-    "vue": "^3.2.47",
+    "vue": "^3.5.13",
     "vue-qr": "^4.0.9",
     "vue-i18n": "^10.0.1",
     "vue-router": "^4.1.6",

+ 4 - 0
src/api/intervalAnalysis.js

@@ -6,6 +6,10 @@ export default{
   classifyListNoChart:params=>{
     return get('/range_analysis/chart_classify/tree',params)
   },
+  //图列表
+  searchChart:params=>{
+    return get('/range_analysis/chart_info/search_by_es',params)
+  },
 
   // 图表刷新
   chartRefresh:params=>{

+ 18 - 0
src/api/reportMaterial.js

@@ -0,0 +1,18 @@
+// 报告素材库
+import { get,post } from "./index";
+
+export default {
+  // 素材库列表
+  materialList(params){
+    return get('/material/list',params)
+  },
+  // 素材库分类
+  classify(params){
+    return get('/material/classify/list',params)
+  },
+  // 图库、表格等模块单个保存至素材库
+  materialOtherSave(params){
+    return post('/material/saveAs',params)
+  }
+
+}

+ 4 - 0
src/assets/svg/up_back.svg

@@ -0,0 +1,4 @@
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3 24C3 12.402 12.402 3 24 3C35.598 3 45 12.402 45 24C45 35.598 35.598 45 24 45C12.402 45 3 35.598 3 24ZM6 24C6 33.9405 14.0595 42 24 42C33.9405 42 42 33.9405 42 24C42 14.0595 33.9405 6 24 6C14.0595 6 6 14.0595 6 24Z" fill="currentColor"/>
+<path d="M24 20.9464L30.894 27.8419C31.0347 27.9825 31.2254 28.0615 31.4243 28.0615C31.6231 28.0615 31.8139 27.9825 31.9545 27.8419L33.015 26.7814C33.0849 26.7118 33.1403 26.629 33.1781 26.5379C33.2159 26.4468 33.2353 26.3491 33.2353 26.2504C33.2353 26.1518 33.2159 26.0541 33.1781 25.963C33.1403 25.8719 33.0849 25.7891 33.015 25.7194L24.531 17.2354C24.4613 17.1656 24.3786 17.1102 24.2875 17.0724C24.1963 17.0346 24.0987 17.0151 24 17.0151C23.9014 17.0151 23.8037 17.0346 23.7126 17.0724C23.6214 17.1102 23.5387 17.1656 23.469 17.2354L22.4085 18.2959L14.9835 25.7209C14.8429 25.8616 14.7639 26.0523 14.7639 26.2512C14.7639 26.4501 14.8429 26.6408 14.9835 26.7814L16.0455 27.8419C16.1862 27.9825 16.3769 28.0615 16.5758 28.0615C16.7746 28.0615 16.9654 27.9825 17.106 27.8419L24 20.9464Z" fill="currentColor"/>
+</svg>

+ 127 - 0
src/components/SaveToMaterial.vue

@@ -0,0 +1,127 @@
+<script setup>
+import moment from 'moment'
+import {ref, watch} from 'vue'
+import apiReportMaterial from '@/api/reportMaterial'
+import { showToast } from 'vant'
+import Classify from '@/views/report/components/reportInsert/reportMaterial/Classify.vue'
+
+const show=defineModel('show',{type:Boolean,default:false})
+
+const props=defineProps({
+  source:{
+    type:String,//来源chart图相关的,excel表格,sandbox逻辑图 , sa_doc文档对比
+    default:''
+  },
+  sourceId:{
+    type:Number,
+    default:0
+  },
+  defaultName:{
+    type:String,//默认的名称
+    default:''
+  }
+})
+
+const name=ref('')
+const classifyId=ref(0)
+const classifyName=ref('')
+const showSelectClassify=ref(false)
+function handleSelectClassify(e){
+  classifyId.value=e.classifyId
+  classifyName.value=e.names.join('/')
+}
+
+async function handleSave(){
+  if(!name.value){
+    showToast('请填写图片名称')
+    return
+  }
+  if(!classifyId.value){
+    showToast('请选择分类')
+    return
+  }
+  const res=await apiReportMaterial.materialOtherSave({
+    ClassifyId:classifyId.value,
+    MaterialName:name.value,
+    ObjectId:props.sourceId,
+    ObjectType:props.source
+  })
+  if(res.Ret!==200) return
+  showToast('保存成功')
+  show.value=false
+}
+
+
+watch(
+  ()=>show.value,
+  (n)=>{
+    if(n){
+      name.value=`${props.defaultName}${moment().format('YYYYMMDD')}`
+    }else{
+      name.value=''
+      classifyId.value=0
+      classifyName.value=''
+    }
+  }
+)
+
+</script>
+
+<template>
+  <van-popup 
+    v-model:show="show" 
+    position="bottom" 
+    round 
+    class="global-pop-wrap_mobile save-to-material-wrap"
+  >
+    <div class="head-box">
+      <span class="cancel-btn" @click="show=false">取消</span>
+      <span class="title">上传素材库</span>
+      <span class="save-btn"  @click="handleSave">保存</span>
+    </div>
+    <div class="content-box">
+      <van-cell title="分类" is-link :value="classifyName||'请选择分类'" @click="showSelectClassify=true" />
+      <van-field v-model="name" label="名称" placeholder="请输入图片名称" />
+    </div>
+  </van-popup>
+  <!-- 选择分类 -->
+  <Classify v-model:show="showSelectClassify" @change="handleSelectClassify"/>
+</template>
+
+<style lang="scss" scoped>
+.save-to-material-wrap{
+  .head-box{
+    display: flex;
+    align-items: center;
+    padding: 32px;
+    .cancel-btn{
+      color: $font-grey;
+    }
+    .save-btn{
+      color: $theme-color;
+    }
+    .title{
+      flex: 1;
+      font-size: 32px;
+      text-align: center;
+    }
+  }
+  .content-box{
+    min-height: 400px;
+    padding: 32px 0;
+    :deep(.van-cell__title){
+      flex: none;
+      box-sizing: border-box;
+      width: var(--van-field-label-width);
+      margin-right: var(--van-field-label-margin-right);
+      color: var(--van-field-label-color);
+      text-align: left;
+    }
+    :deep(.van-field__control){
+      text-align: right;
+    }
+  }
+  
+
+}
+</style>

+ 25 - 2
src/views/chartETA/ChartDetail.vue

@@ -16,8 +16,11 @@ import {useCachedViewsStore} from '@/store/modules/cachedViews'
 import { setExtremumDate } from '@/hooks/chart/commonFun.js'
 import {chartLibBtn,useAuthBtn} from '@/hooks/useAuthBtn'
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
-const {checkAuthBtn} = useAuthBtn()
 import _ from 'lodash';
+import SaveToMaterial from '@/components/SaveToMaterial.vue'
+
+
+const {checkAuthBtn} = useAuthBtn()
 
 const publicSettingStore = usePublicSettingStore()
 const {options,axisLimitState,chartRender,setLimitData,isUseSelfLimit}=useChartRender()
@@ -294,9 +297,15 @@ function getChartActions(chartInfo){
         },
         {
             type:'savePic',
-            label:'保存图片',
+            label:'保存至相册',
             show:true,
         },
+        {
+            type:'saveToMaterial',
+            label:'保存为素材',
+            show:true,
+            btnCode:chartLibBtn.chartLib_enNameSetting
+        },
         {
             type:'setEnName',
             label:'设置英文名称',
@@ -340,6 +349,7 @@ function handleActionClick(action){
         'addToMyETA':openAddToMyETADialog,
         'delete':deleteChart,
         'save':saveChart,
+        'saveToMaterial':handleShowSaveToMaterial
     }
     eventMap[action.type]()
     //showMoreAction.value = false
@@ -615,6 +625,12 @@ function deleteChart(){
         
     })
 }
+// 保存至素材库
+const showSaveToMaterial=ref(false)
+function handleShowSaveToMaterial(){
+    showSaveToMaterial.value=true
+}
+
 
 //获取图排序列表数据
 const chartSortListData=ref([])
@@ -1040,6 +1056,13 @@ function openDateSelect(){
                 @close="closeSetNameDialog"
             />
         </van-popup>
+        <!-- 保存至素材库 -->
+        <SaveToMaterial
+            v-model:show="showSaveToMaterial"
+            source="chart"
+            :sourceId="chartInfo.ChartInfoId"
+            :defaultName="chartInfo.ChartName"
+        />
 
     </div>
 </template>

+ 18 - 2
src/views/myETA/ChartDetail.vue

@@ -28,8 +28,10 @@ import { setExtremumDate } from '@/hooks/chart/commonFun.js'
 import {myETABtn,useAuthBtn} from '@/hooks/useAuthBtn'
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
 import { useNoAuth } from '@/hooks/useNoAuth'
-const {checkAuthBtn} = useAuthBtn()
+import SaveToMaterial from '@/components/SaveToMaterial.vue'
+
 
+const {checkAuthBtn} = useAuthBtn()
 
 const publicSettingStore = usePublicSettingStore()
 const isMoreActionShow = computed(()=>{
@@ -758,6 +760,9 @@ function handleEditEnNameSuccess(){
 // 加入我的图库
 const isShowAddToMyETADialog=ref(false)
 
+// 保存至素材库
+const showSaveToMaterial=ref(false)
+
 </script>
 
 <template>
@@ -1077,7 +1082,10 @@ const isShowAddToMyETADialog=ref(false)
                 另存为
             </div>
             <div class="item" @click.stop="handleChartSavePicture" v-if="!chartInfo.Disabled&&checkAuthBtn(myETABtn.myChart_copyWechat)&&checkAuthBtn(myETABtn.myChart_copyOffice)">
-                保存图片
+                保存至相册
+            </div>
+            <div class="item" @click.stop="showSaveToMaterial=true" v-if="!chartInfo.Disabled&&checkAuthBtn(myETABtn.myChart_copyWechat)&&checkAuthBtn(myETABtn.myChart_copyOffice)">
+                保存为素材
             </div>
             <div class="item" @click.stop="handleShowEditEnName" v-permission="myETABtn.myChart_enNameSetting" v-if="chartInfo.Source!==11">
                 设置英文名称
@@ -1155,6 +1163,14 @@ const isShowAddToMyETADialog=ref(false)
         :chartInfo="chartInfo"
         @close="isShowAddToMyETADialog=false"
     />
+
+    <!-- 保存至素材库 -->
+    <SaveToMaterial
+        v-model:show="showSaveToMaterial"
+        source="chart"
+        :sourceId="chartInfo.ChartInfoId"
+        :defaultName="chartInfo.ChartName"
+    />
 </template>
 
 <style lang="scss" scoped>

+ 233 - 0
src/views/report/components/reportInsert/BalanceSheet/ChildSheet.vue

@@ -0,0 +1,233 @@
+<script setup>
+import { watch, ref } from "vue";
+import apiSheetChart from "@/api/sheet";
+
+const show = defineModel("show", { type: Boolean, default: false });
+const emits=defineEmits(['insert'])
+
+const props = defineProps({
+  sheetData: {
+    type: [null, Object],
+    default: null,
+  },
+});
+
+// 获取表格的所有版本
+const showSelectVersion = ref(false);
+const versionOpts = ref([]);
+const version = ref("");
+async function getSheetVersion() {
+  const res = await apiSheetChart.balanceTableVersion({
+    ExcelInfoId: props.sheetData.ExcelInfoId,
+  });
+  if (res.Ret === 200) {
+    const arr = res.Data.List || [];
+    versionOpts.value = arr.map((i) => {
+      return {
+        ...i,
+        name: i.VersionName,
+      };
+    });
+  }
+}
+// 点击选择某个版本
+function handleSelectVersion(e) {
+  version.value = e.ExcelInfoId;
+  getChildSheetList();
+  showSelectVersion.value=false
+}
+
+// 获取当前版本下所有子表
+const childSheetList = ref([]);
+async function getChildSheetList() {
+  const res = await apiSheetChart.getBalanceChildTable({
+    ParentId: version.value,
+  });
+  if (res.Ret === 200) {
+    childSheetList.value = res.Data.List || [];
+  }
+}
+
+// 选择表格
+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(
+  () => show.value,
+  (n) => {
+    if (n) {
+      version.value = props.sheetData.ExcelInfoId;
+      getSheetVersion();
+      getChildSheetList();
+    }
+  }
+);
+
+
+function handleConfirmInsert(){
+  show.value=false
+  emits('insert',selectChartList.value)
+}
+</script>
+
+<template>
+  <van-popup v-model:show="show" position="bottom" round class="child-sheet-wrap">
+    <div class="title-box">
+      <span class="back-btn" @click="show = false">返回</span>
+      <span class="title">{{ props.sheetData.ExcelName }}</span>
+    </div>
+    <van-cell title="版本" :value="versionOpts.filter((i) => i.ExcelInfoId === version)[0]?.VersionName" is-link @click="showSelectVersion = true" />
+    <div class="content-box">
+      <div v-if="childSheetList.length == 0">
+        <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">
+        <li :class="['chart-item', selectChartList.includes(item.UniqueCode) && 'active']" v-for="item in childSheetList" :key="item.UniqueCode" @click="handleSelect(item)">
+          <div class="van-multi-ellipsis--l2 title">{{ item.ExcelName }}</div>
+          <img :src="item.ExcelImage" 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 class="bot-btn">
+      <van-button type="primary" block @click="handleConfirmInsert" :disabled="selectChartList.length===0">插入</van-button>
+    </div>
+  </van-popup>
+
+  <!-- 选择版本 -->
+  <van-action-sheet v-model:show="showSelectVersion" :actions="versionOpts" @select="handleSelectVersion" />
+</template>
+
+<style lang="scss" scoped>
+.child-sheet-wrap {
+  height: 70vh;
+  display: flex;
+  flex-direction: column;
+  .title-box {
+    height: 116px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: relative;
+    border-bottom: 1px solid $border-color;
+    .title {
+      font-size: 36px;
+    }
+    .back-btn {
+      position: absolute;
+      top: 50%;
+      left: 32px;
+      transform: translateY(-50%);
+      color: $font-grey_999;
+    }
+  }
+  .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;
+    align-content: flex-start;
+    .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;
+      }
+    }
+  }
+  .bot-btn{
+    padding: 48px;
+  }
+}
+@media screen and (min-width:$media-width){
+  .child-sheet-wrap{
+    .title-box{
+      height: 58px;
+      .title{
+        font-size: 18px;
+      }
+      .back-btn{
+        left: 16px;
+      }
+    }
+    .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;
+                }
+            }
+    }
+    .bot-btn{
+      padding: 24px;
+    }
+  }
+
+}
+</style>

+ 191 - 0
src/views/report/components/reportInsert/BalanceSheet/Index.vue

@@ -0,0 +1,191 @@
+<script setup>
+import {reactive, ref,watch} from 'vue'
+import apiSheetChart from '@/api/sheet'
+import { vInfiniteScroll } from '@vueuse/components'
+import { showToast } from 'vant'
+import { useNoAuth } from '@/hooks/useNoAuth'
+import ChildSheet from './ChildSheet.vue'
+
+
+const emits=defineEmits(['update','insert'])
+
+const searchVal=ref('')
+const onlyMe=ref(false)
+const listState=reactive({
+    page:1,
+    pageSize:20,
+    list:[],
+    finished:false,
+    loading:false
+})
+async function getSheetList(){
+    const params={
+        Keyword:searchVal.value,
+        CurrentIndex:listState.page,
+        PageSize:listState.pageSize,
+        Source:5,
+        IsShowMe:onlyMe.value
+    }
+    listState.loading=true
+    let res=await apiSheetChart.sheetList(params)
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data.List||[]
+        listState.list=[...listState.list,...arr]
+        listState.finished=res.Data?.Paging.IsEnd
+    }
+}
+getSheetList()
+// 触底加载更多
+function onLoadMore(){
+    if(listState.finished||listState.loading) return
+    listState.page++
+    getSheetList()
+}
+function handleRefreshList(){
+    listState.list=[]
+    listState.page=1
+    listState.finished=false
+    getSheetList()
+}
+
+const selectChartList=ref([])
+const showChildSheet=ref(false)
+const activeSheetData=ref(null)
+function handleSelect(item){
+    if(!item.HaveOperaAuth) return showToast(useNoAuth().sheet)
+    activeSheetData.value=item
+    showChildSheet.value=true    
+}
+
+function handleInsert(e){
+    emits('update',e)
+    emits('insert')
+}
+</script>
+
+<template>
+    <div class="sheet-insert-wrap">
+        <div class="sticky-box">
+            <van-search v-model="searchVal" shape="round" placeholder="请输入表格名称" @search="handleRefreshList" @clear="handleRefreshList" />
+            <van-checkbox v-model="onlyMe" @change="handleRefreshList">只看我的</van-checkbox>
+        </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.UniqueCode"
+                    @click="handleSelect(item)"
+                >
+                    <div class="van-multi-ellipsis--l2 title">{{item.ExcelName}}</div>
+                    <img :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ExcelImage" alt="">
+                </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>
+    <!-- 子平衡表 -->
+    <ChildSheet v-model:show="showChildSheet" :sheetData="activeSheetData" @insert="handleInsert"/>
+  
+</template>
+
+<style lang="scss" scoped>
+.sheet-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;
+        align-content: flex-start;
+        .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;
+    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){
+    .sheet-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>

+ 1 - 23
src/views/report/components/reportInsert/ETAChart.vue

@@ -2,10 +2,6 @@
 import {reactive, ref, watch} from 'vue'
 import apiChart from '@/api/chart'
 import apiFutureChart from '@/api/futureChart'
-import apiCorrelationChart from '@/api/correlationChart'
-import apiLineEquationChart from '@/api/lineEquationChart'
-import apiStatisticFeatureChart from '@/api/statisticFeatureChart'
-import apiCrossVarietyChart from '@/api/crossVarietyChart'
 import { showToast } from 'vant'
 import { vInfiniteScroll } from '@vueuse/components'
 import { useNoAuth } from '@/hooks/useNoAuth'
@@ -13,7 +9,6 @@ import { useNoAuth } from '@/hooks/useNoAuth'
 const emits=defineEmits(['update'])
 
 const searchVal=ref('')
-const typeActive=ref(1)
 const onlyMe=ref(false)
 
 const listState=reactive({
@@ -32,17 +27,8 @@ async function getChartList(){
         IsShowMe:onlyMe.value
     }
     listState.loading=true
-
-    const apiMap = {
-        1: apiChart.ETAChartListByES,
-        2: apiFutureChart.searchChartList,
-        3: apiCorrelationChart.searchChartList,
-        6: apiLineEquationChart.searchChartList,
-        7: apiStatisticFeatureChart.searchChart,
-        10: apiCrossVarietyChart.searchChart,
-    }
     
-    let res = await apiMap[typeActive.value](params)
+    let res = await apiChart.ETAChartListByES(params)
     
     listState.loading=false
     if(res.Ret===200){
@@ -101,14 +87,6 @@ watch(
     <div class="ETA-chart-wrap">
         <div class="sticky-box">
             <van-search v-model="searchVal" shape="round" placeholder="请输入图表名称" @search="handleRefreshList" @clear="handleRefreshList" />
-            <van-tabs v-model:active="typeActive" line-width="16px" title-active-color="#0052D9" @change="handleRefreshList">
-                <van-tab title="图库" :name="1"></van-tab>
-                <van-tab title="商品价格曲线" :name="2"></van-tab>
-                <van-tab title="相关性图表" :name="3"></van-tab>
-                <van-tab title="拟合方程曲线" :name="6"></van-tab>
-                <van-tab title="统计特征" :name="7"></van-tab>
-                <van-tab title="跨品种分析" :name="10"></van-tab>
-            </van-tabs>
             <van-checkbox v-model="onlyMe" @change="handleRefreshList">只看我的</van-checkbox>
         </div>
         <div class="content-box">

+ 88 - 9
src/views/report/components/reportInsert/Index.vue

@@ -1,15 +1,89 @@
 <script setup>
 import {ref} from 'vue'
+import {apiMenuList} from '@/api/user'
 import ETAChart from './ETAChart.vue'
 import MyETAChart from './MyETAChart.vue'
 import SandTableImg from './SandTableImg.vue'
 import SheetTableChart from './SheetTableChart.vue'
 import SemanticsImg from './SemanticsImg.vue'
+import MaterialImg from './reportMaterial/Index.vue'
+import StatisticAnalysis from './StatisticAnalysis.vue'
+import PriceChart from './PriceChart.vue'
+import BalanceSheet from './BalanceSheet/Index.vue'
 
 const emits=defineEmits(['insert'])
 
-const typeOpt=['图表插入','批量插入','表格插入','沙盘插入','语义分析插入']
-const activeType=ref('图表插入')
+// const typeOpt=['素材库','图表插入','批量插入','表格插入','沙盘插入','语义分析插入']
+const typeOpt=ref([
+    {
+        value:'素材库',
+        label:'素材库',
+        path:'materialList'
+    },
+    {
+        value:'图表插入',
+        label:'图库',
+        path:''
+    },
+    {
+        value:'批量插入',
+        label:'我的图库',
+        path:'mychart'
+    },
+    {
+        value:'表格插入',
+        label:'表格',
+        path:''
+    },
+    {
+        value:'平衡表',
+        label:'平衡表',
+        path:'sheetBalanceList'
+    },
+    {
+        value:'统计分析',
+        label:'统计分析',
+        path:''
+    },
+    {
+        value:'商品价格曲线',
+        label:'商品价格曲线',
+        path:'commordityChartBase'
+    },
+    {
+        value:'沙盘插入',
+        label:'逻辑图',
+        path:'sandlist'
+    },
+    {
+        value:'语义分析插入',
+        label:'语义分析',
+        path:'semanticsPage'
+    },
+])
+const activeType=ref('素材库')
+async function initTypeOpts(){
+    const res=await apiMenuList()
+    if(res.Ret===200){
+        const menuData=res.Data.List||[]
+        let pathArr=[]
+        menuData.forEach(item=>{
+			if(item.children){
+				item.children.forEach(_item=>{
+					pathArr.push(_item.path)
+				})
+			}else{
+				pathArr.push(item.path)
+			}
+		})
+        typeOpt.value=typeOpt.value.filter(i=>!i.path||pathArr.includes(i.path))
+        if(typeOpt.value.length>0){
+            activeType.value=typeOpt.value[0].value
+        }
+    }
+}
+initTypeOpts()
+
 
 let list=ref([])
 
@@ -18,14 +92,14 @@ function handleSelectChart(data){
 }
 
 function handleConfirmInsert(){
-    if(['图表插入','批量插入'].includes(activeType.value)){
+    if(['图表插入','批量插入','统计分析','商品价格曲线'].includes(activeType.value)){
         console.log(list.value)
         let filterList = list.value;
 
         emits('insert',{list: filterList,type:'iframe',chartType:'chart'})
-    }else if(activeType.value==='表格插入'){
+    }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:''})
     }
     
@@ -37,11 +111,11 @@ function handleConfirmInsert(){
     <div class="report-insert-content-wrap">
         <ul class="top-type-box">
             <li 
-                :class="['item',activeType===item&&'active']"
+                :class="['item',activeType===item.value&&'active']"
                 v-for="item in typeOpt" 
                 :key="item"
-                @click="activeType=item;list=[]"
-            >{{item}}</li>
+                @click="activeType=item.value;list=[]"
+            >{{item.label}}</li>
         </ul>
         <div class="main-box">
             <ETAChart @update="handleSelectChart" v-if="activeType==='图表插入'"/>
@@ -49,8 +123,13 @@ function handleConfirmInsert(){
             <SheetTableChart @update="handleSelectChart" v-if="activeType==='表格插入'"/>
             <SandTableImg @update="handleSelectChart" v-if="activeType==='沙盘插入'"/>
             <SemanticsImg @update="handleSelectChart" v-if="activeType==='语义分析插入'"/>
+            <MaterialImg @update="handleSelectChart" v-if="activeType==='素材库'"/>
+            <StatisticAnalysis @update="handleSelectChart" v-if="activeType==='统计分析'"/>
+            <PriceChart @update="handleSelectChart" v-if="activeType==='商品价格曲线'"/>
+            <BalanceSheet @update="handleSelectChart" @insert="handleConfirmInsert" v-if="activeType==='平衡表'"/>
+            
         </div>
-        <div class="bot-btn">
+        <div class="bot-btn" v-if="activeType!=='平衡表'">
             <van-button type="primary" block @click="handleConfirmInsert" :disabled="list.length===0">插入</van-button>
         </div>
     </div>

+ 208 - 0
src/views/report/components/reportInsert/PriceChart.vue

@@ -0,0 +1,208 @@
+<script setup>
+import {reactive, ref, watch} from 'vue'
+import apiFutureChart from '@/api/futureChart'
+import { showToast } from 'vant'
+import { vInfiniteScroll } from '@vueuse/components'
+import { useNoAuth } from '@/hooks/useNoAuth'
+
+const emits=defineEmits(['update'])
+
+const searchVal=ref('')
+const onlyMe=ref(false)
+
+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,
+        IsShowMe:onlyMe.value
+    }
+    listState.loading=true
+    
+    let res = await apiFutureChart.searchChartList(params)
+    
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data.List||[]
+        listState.list=[...listState.list,...arr]
+        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){
+    if(item.Disabled){
+        showToast('内部图表,不允许插入报告')
+        return
+    }
+    if(!item.HaveOperaAuth) return showToast(useNoAuth().chart)
+    
+    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" />
+            <van-checkbox v-model="onlyMe" @change="handleRefreshList">只看我的</van-checkbox>
+        </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">{{item.ChartName}}</div>
+                    <img :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ChartImage" 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>

+ 67 - 4
src/views/report/components/reportInsert/SheetTableChart.vue

@@ -1,13 +1,33 @@
 <script setup>
-import {reactive, ref,watch} from 'vue'
+import { reactive, ref,watch} from 'vue'
 import apiSheetChart from '@/api/sheet'
+import {apiMenuList} from '@/api/user'
 import { vInfiniteScroll } from '@vueuse/components'
 import { showToast } from 'vant'
 import { useNoAuth } from '@/hooks/useNoAuth'
 
 const emits=defineEmits(['update'])
 
+const typeOpts=ref([
+    {
+        label:'共享表格',
+        value:1,
+        path:'sheetList'
+    },
+    {
+        label:'时间序列表格',
+        value:2,
+        path:'sheetTimeList'
+    },
+    {
+        label:'混合表格',
+        value:3,
+        path:'sheetMixedList'
+    }
+])
 const searchVal=ref('')
+const typeActive=ref(1)
+const onlyMe=ref(false)
 const listState=reactive({
     page:1,
     pageSize:20,
@@ -19,7 +39,9 @@ async function getSheetList(){
     const params={
         Keyword:searchVal.value,
         CurrentIndex:listState.page,
-        PageSize:listState.pageSize
+        PageSize:listState.pageSize,
+        Source:typeActive.value,
+        IsShowMe:onlyMe.value
     }
     listState.loading=true
     let res=await apiSheetChart.sheetList(params)
@@ -30,7 +52,7 @@ async function getSheetList(){
         listState.finished=res.Data?.Paging.IsEnd
     }
 }
-getSheetList()
+
 // 触底加载更多
 function onLoadMore(){
     if(listState.finished||listState.loading) return
@@ -44,6 +66,29 @@ function handleRefreshList(){
     getSheetList()
 }
 
+async function initTypeOpts(){
+    const res=await apiMenuList()
+    if(res.Ret===200){
+        const menuData=res.Data.List||[]
+        let pathArr=[]
+        menuData.forEach(item=>{
+			if(item.children){
+				item.children.forEach(_item=>{
+					pathArr.push(_item.path)
+				})
+			}else{
+				pathArr.push(item.path)
+			}
+		})
+        typeOpts.value=typeOpts.value.filter(i=>!i.path||pathArr.includes(i.path))
+        if(typeOpts.value.length>0){
+            typeActive.value=typeOpts.value[0].value
+            getSheetList()
+        }
+    }
+}
+initTypeOpts()
+
 const selectChartList=ref([])
 function handleSelect(item){
     if(!item.HaveOperaAuth) return showToast(useNoAuth().sheet)
@@ -70,7 +115,14 @@ watch(
 
 <template>
     <div class="sheet-insert-wrap">
-        <van-search v-model="searchVal" shape="round" placeholder="请输入图表名称" @search="handleRefreshList" @clear="handleRefreshList" />
+        <div class="sticky-box">
+            <van-search v-model="searchVal" shape="round" placeholder="请输入表格名称" @search="handleRefreshList" @clear="handleRefreshList" />
+            <van-tabs v-model:active="typeActive" :ellipsis="false" line-width="16px" title-active-color="#0052D9" @change="handleRefreshList">
+                <van-tab :title="item.label" :name="item.value" v-for="item in typeOpts" :key="item.value"></van-tab>
+            </van-tabs>
+            <van-checkbox v-model="onlyMe" @change="handleRefreshList">只看我的</van-checkbox>
+        </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="">
@@ -116,6 +168,7 @@ watch(
         display: flex;
         flex-wrap: wrap;
         justify-content: space-between;
+        align-content: flex-start;
         .chart-item{
             width: 48%;
             background: #FFFFFF;
@@ -149,6 +202,16 @@ watch(
         }
     }
 }
+.sticky-box{
+    flex-shrink: 0;
+    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){
     .sheet-insert-wrap{
         .chart-list{

+ 276 - 0
src/views/report/components/reportInsert/StatisticAnalysis.vue

@@ -0,0 +1,276 @@
+<script setup>
+import {reactive, ref, watch} from 'vue'
+import apiCorrelationChart from '@/api/correlationChart'
+import apiLineEquationChart from '@/api/lineEquationChart'
+import apiStatisticFeatureChart from '@/api/statisticFeatureChart'
+import apiCrossVarietyChart from '@/api/crossVarietyChart'
+import apiIntervalAnalysis from '@/api/intervalAnalysis'
+import { showToast } from 'vant'
+import { vInfiniteScroll } from '@vueuse/components'
+import { useNoAuth } from '@/hooks/useNoAuth'
+import {apiMenuList} from '@/api/user'
+
+const emits=defineEmits(['update'])
+
+const typeOpts=ref([
+  {
+    label:'相关性',
+    value:3,
+    path:'chartrelevance'
+  },
+  {
+    label:'拟合方程曲线',
+    value:6,
+    path:'fittingEquationList'
+  },
+  {
+    label:'统计特征',
+    value:7,
+    path:'statisticFeatureList'
+  },
+  {
+    label:'跨品种分析',
+    value:10,
+    path:'crossVarietyChartList'
+  },
+  {
+    label:'区间分析',
+    value:12,
+    path:'rangeAnalysis'
+  },
+])
+
+const searchVal=ref('')
+const typeActive=ref(3)
+const onlyMe=ref(false)
+
+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,
+        IsShowMe:onlyMe.value
+    }
+    listState.loading=true
+
+    const apiMap = {
+        3: apiCorrelationChart.searchChartList,
+        6: apiLineEquationChart.searchChartList,
+        7: apiStatisticFeatureChart.searchChart,
+        10: apiCrossVarietyChart.searchChart,
+        12: apiIntervalAnalysis.searchChart,
+    }
+    
+    let res = await apiMap[typeActive.value](params)
+    
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data.List||[]
+        listState.list=[...listState.list,...arr]
+        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()
+}
+
+async function initTypeOpts(){
+    const res=await apiMenuList()
+    if(res.Ret===200){
+        const menuData=res.Data.List||[]
+        let pathArr=[]
+        menuData.forEach(item=>{
+			if(item.children){
+				item.children.forEach(_item=>{
+					pathArr.push(_item.path)
+				})
+			}else{
+				pathArr.push(item.path)
+			}
+		})
+        typeOpts.value=typeOpts.value.filter(i=>!i.path||pathArr.includes(i.path))
+        if(typeOpts.value.length>0){
+            typeActive.value=typeOpts.value[0].value
+            getChartList()
+        }
+    }
+}
+initTypeOpts()
+
+const selectChartList=ref([])
+function handleSelect(item){
+    if(item.Disabled){
+        showToast('内部图表,不允许插入报告')
+        return
+    }
+    if(!item.HaveOperaAuth) return showToast(useNoAuth().chart)
+    
+    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" />
+            <van-tabs v-model:active="typeActive" :ellipsis="false" line-width="16px" title-active-color="#0052D9" @change="handleRefreshList">
+                <van-tab v-for="item in typeOpts" :key="item.value" :title="item.label" :name="item.value"></van-tab>
+            </van-tabs>
+            <van-checkbox v-model="onlyMe" @change="handleRefreshList">只看我的</van-checkbox>
+        </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">{{item.ChartName}}</div>
+                    <img :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ChartImage" 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>

+ 121 - 0
src/views/report/components/reportInsert/reportMaterial/Classify.vue

@@ -0,0 +1,121 @@
+<script setup>
+import {ref} from 'vue'
+import apiReportMaterial from '@/api/reportMaterial'
+import { showToast } from 'vant'
+
+const show = defineModel('show', { required: true,type:Boolean,default:false })
+const emits=defineEmits(['change'])
+
+const classifyId=ref('')
+const classifyOpts=ref([])
+function removeEmptyChildrenProperties(nodes) {
+    return nodes.map(node => {
+        // 深拷贝节点
+        const newNode = { ...node };
+
+        // 如果 Children 是空数组,则删除该属性
+        if (Array.isArray(newNode.Children) && newNode.Children.length === 0) {
+            delete newNode.Children;
+        } else if (newNode.Children && newNode.Children.length > 0) {
+            // 如果 Children 非空,递归处理子节点
+            newNode.Children = removeEmptyChildrenProperties(newNode.Children);
+        }
+
+        return newNode;
+    });
+}
+async function getClassifyList(){
+  const res=await apiReportMaterial.classify()
+  if(res.Ret===200){
+    const arr=res.Data.AllNodes||[]
+    classifyOpts.value=removeEmptyChildrenProperties(arr)
+  }
+}
+getClassifyList()
+
+let selectedOpt=[]
+function handleSelectChange({value,selectedOptions,tabIndex}){
+  selectedOpt=selectedOptions
+}
+
+function handleConfirm(){
+  if(!classifyId.value){
+    showToast('请选择分类')
+    return
+  }
+  // console.log(selectedOpt);
+  const names=[]
+  selectedOpt.forEach(item=>{
+    names.push(item.ClassifyName)
+  })
+  emits('change',{names:names,classifyId:classifyId.value})
+  show.value=false
+}
+
+</script>
+
+<template>
+  <van-popup 
+    v-model:show="show" 
+    position="bottom" 
+    round
+    class="classify-wrap"
+  > 
+    <div class="top-box">
+      <span class="cancel-btn" @click="show=false">取消</span>
+      <span class="title">选择分类</span>
+      <span class="confirm-btn" @click="handleConfirm">确定</span>
+    </div>
+    <van-cascader
+      v-model="classifyId"
+      :options="classifyOpts"
+      :closeable="false"
+      :show-header="false"
+      :field-names="{text:'ClassifyName',value:'ClassifyId',children:'Children'}"
+      @change="handleSelectChange"
+    />
+  </van-popup>
+</template>
+
+<style lang="scss" scoped>
+.classify-wrap{
+  height: 70vh;
+  display: flex;
+  flex-direction: column;
+  .top-box{
+    display: flex;
+    align-items: center;
+    padding: 32px 34px;
+    border-bottom: 1px solid $border-color;
+    .cancel-btn{
+      color: $font-grey;
+      font-size: 32px;
+    }
+    .confirm-btn{
+      color: $theme-color;
+      font-size: 32px;
+    }
+    .title{
+      font-size: 36px;
+      flex: 1;
+      text-align: center;
+    }
+  }
+}
+@media screen and (min-width:$media-width){
+  .classify-wrap{
+    .top-box{
+      padding: 16px 17px;
+      .cancel-btn{
+        font-size: 16px;
+      }
+      .confirm-btn{
+        font-size: 16px;
+      }
+      .title{
+        font-size: 18px;
+      }
+    }
+  }
+}
+</style>

+ 235 - 0
src/views/report/components/reportInsert/reportMaterial/Index.vue

@@ -0,0 +1,235 @@
+<script setup>
+import {reactive, ref,watch} from 'vue'
+import apiReportMaterial from '@/api/reportMaterial'
+import { vInfiniteScroll } from '@vueuse/components'
+import Classify from './Classify.vue'
+import moment from 'moment'
+
+const emits=defineEmits(['update'])
+
+const classifyName=ref('')
+const classifyId=ref('')
+const searchVal=ref('')
+const listState=reactive({
+    page:1,
+    pageSize:20,
+    list:[],
+    finished:false,
+    loading:false
+})
+async function getList(){
+    const params={
+        Keyword:searchVal.value,
+        CurrentIndex:listState.page,
+        PageSize:listState.pageSize,
+        ClassifyId:classifyId.value
+    }
+    listState.loading=true
+    let res=await apiReportMaterial.materialList(params)
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data.List||[]
+        listState.list=[...listState.list,...arr]
+        listState.finished=res.Data?.Paging.IsEnd
+    }
+}
+getList()
+// 触底加载更多
+function onLoadMore(){
+    if(listState.finished||listState.loading) return
+    listState.page++
+    getList()
+}
+function handleRefreshList(){
+    listState.list=[]
+    listState.page=1
+    listState.finished=false
+    getList()
+}
+
+let selectChartList=ref([])
+function handleSelect(item){
+    const index=selectChartList.value.indexOf(item.MaterialId)
+    if(index!==-1){
+        selectChartList.value.splice(index,1)
+    }else{
+        selectChartList.value.push(item.MaterialId)
+    }
+    
+}
+
+watch(
+    ()=>selectChartList.value,
+    ()=>{
+        const arr=listState.list.filter(item=>selectChartList.value.includes(item.MaterialId)).map(e=>e.ImgUrl)
+        emits('update',arr)
+    },
+    {
+        immediate:true,
+        deep:true
+    }
+)
+
+const showClassify=ref(false)
+function handleClassifyChange(e){
+    classifyId.value=e.classifyId
+    classifyName.value=e.names.join('/')
+    handleRefreshList()
+}
+
+</script>
+
+
+<template>
+    <div class="sandTableImg-insert-wrap">
+        <div class="top-box">
+          <van-search style="flex:1" v-model="searchVal" shape="round" placeholder="请输入图片名称" @search="handleRefreshList" @clear="handleRefreshList" />
+          <img class="img-icon" src="@/assets/imgs/myETA/icon_menu2.png" alt="" @click="showClassify=true">
+        </div>
+        
+        <div class="content-box">
+            <div>{{classifyName}}</div>
+            <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.MaterialId)&&'active']" 
+                    v-for="item in listState.list" 
+                    :key="item.MaterialId"
+                    @click="handleSelect(item)"
+                >
+                    <div class="van-multi-ellipsis--l2 title">{{item.MaterialName}}</div>
+                    <img :src="item.ImgUrl" alt="">
+                    <div class="time-box">
+                        <span>{{moment(item.CreateTime).format('YYYY-MM-DD')}}</span>
+                        <span>{{item.SysUserRealName}}</span>
+                    </div>
+
+                    <svg v-if="selectChartList.includes(item.MaterialId)" 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>
+
+    <!-- 分类 -->
+    <Classify v-model:show="showClassify" @change="handleClassifyChange"/>
+</template>
+
+<style lang="scss" scoped>
+.sandTableImg-insert-wrap{
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    .top-box{
+      display: flex;
+      align-items: center;
+      padding-right: 32px;
+      .img-icon{
+        width: 68px;
+        height: 68px;
+        flex-shrink: 0;
+      }
+    }
+    .content-box{
+        overflow: hidden;
+        flex: 1;
+        padding: var(--van-padding-sm);
+        min-height: 300PX;
+    }
+    .chart-list{
+        margin-top: 10px;
+        height: 100%;
+        overflow-y: auto;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: space-between;
+        align-content: flex-start;
+        .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;
+            }
+            .time-box{
+                display: flex;
+                justify-content: space-between;
+                font-size: 24px;
+                color: $font-grey;
+            }
+        }
+        .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{
+        .top-box{
+            padding-right: 16px;
+            .img-icon{
+                width: 34px;
+                height: 34px;
+            }
+        }
+        .chart-list{
+            height: auto;
+            max-height: 100%;
+            margin-top: 5px;
+            .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;
+                }
+                .time-box{
+                    font-size: 12px;
+                }
+            }
+            .active{
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+}
+</style>

+ 1 - 1
src/views/report/smartReport/EditReport.vue

@@ -1070,7 +1070,7 @@ const {
 				@confirm="handleInsertImg"
 			/>
 			<!-- 图表资源 -->
-			<ReportInsertContent v-if="showInsertCompType===3" @insert="handleChartInsert"/>
+			<ReportInsertContent v-if="showInsertCompType===3&&showReportInsertPop" @insert="handleChartInsert"/>
 	</van-popup>
 
   <!-- 报告基础信息 -->