瀏覽代碼

完成研报编辑新增

jwyu 1 年之前
父節點
當前提交
2485caa5ca

+ 7 - 0
src/api/chart.js

@@ -133,6 +133,13 @@ export default{
      */
      */
     ETAChartListByES:params=>{
     ETAChartListByES:params=>{
         return get('/datamanage/chart_info/search_by_es',params)
         return get('/datamanage/chart_info/search_by_es',params)
+    },
+    /**
+     * 多图表一键刷新
+     * @param ChartInfoCode
+     */
+    refreshChartMultiple(params){
+        return post('/datamanage/chart_info/batch_refresh',params)
     }
     }
 
 
 } 
 } 

+ 66 - 26
src/api/correlationChart.js

@@ -1,32 +1,72 @@
 //相关性图表模块
 //相关性图表模块
 import { get,post } from "./index";
 import { get,post } from "./index";
 
 
-/**
- * 相关性图表列表
- * @param PageSize
- * @param CurrentIndex
- * @param KeyWord
- * @param IsShowMe
- */
-export function apiCorrelationChartList(params){
-    return get('/correlation/chart_info/list',params)
-}
+// /**
+//  * 相关性图表列表
+//  * @param PageSize
+//  * @param CurrentIndex
+//  * @param KeyWord
+//  * @param IsShowMe
+//  */
+// export function apiCorrelationChartList(params){
+//     return get('/correlation/chart_info/list',params)
+// }
 
 
-/**
- * 获取相关性图表分类数据
- * @param IsShowMe
- * @param Source
- */
-export function apiCorrelationChartClassifyList(params){
-    return get('/correlation/chart_classify/list',params)
-}
+// /**
+//  * 获取相关性图表分类数据
+//  * @param IsShowMe
+//  * @param Source
+//  */
+// export function apiCorrelationChartClassifyList(params){
+//     return get('/correlation/chart_classify/list',params)
+// }
 
 
-/**
- * 相关性图表另存为
- * @param ChartInfoId
- * @param ChartClassifyId
- * @param ChartName
- */
-export function apiCorrelationChartSaveOther(params){
-    return post('/correlation/chart_info/copy',params)
+// /**
+//  * 相关性图表另存为
+//  * @param ChartInfoId
+//  * @param ChartClassifyId
+//  * @param ChartName
+//  */
+// export function apiCorrelationChartSaveOther(params){
+//     return post('/correlation/chart_info/copy',params)
+// }
+
+export default {
+    /**
+     * 相关性图表列表
+     * @param PageSize
+     * @param CurrentIndex
+     * @param KeyWord
+     * @param IsShowMe
+     */
+    chartList(params){
+        return get('/correlation/chart_info/list',params)
+    },
+    /**
+     * 获取相关性图表分类数据
+     * @param IsShowMe
+     * @param Source
+     */
+    chartClassifyList(params){
+        return get('/correlation/chart_classify/list',params)
+    },
+    /**
+     * 相关性图表另存为
+     * @param ChartInfoId
+     * @param ChartClassifyId
+     * @param ChartName
+     */
+    chartSaveOther(params){
+        return post('/correlation/chart_info/copy',params)
+    },
+    /**
+     * 搜索相关性图表
+     * @param PageSize
+     * @param CurrentIndex
+     * @param KeyWord
+     * @param IsShowMe
+     */
+    searchChartList(params){
+        return get('/correlation/chart_info/search_by_es',params)
+    },
 }
 }

+ 52 - 24
src/api/futureChart.js

@@ -1,30 +1,58 @@
 // 商品价格曲线模块
 // 商品价格曲线模块
 import { get,post } from "./index";
 import { get,post } from "./index";
 
 
-/**
- * 搜索图表
- * @param PageSize
- * @param CurrentIndex
- * @param KeyWord
- * @param IsShowMe
- */
-export function apiFutureSearchChartList(params){
-    return get('/future_good/chart_info/search_by_es',params)
-}
+// /**
+//  * 搜索图表
+//  * @param PageSize
+//  * @param CurrentIndex
+//  * @param KeyWord
+//  * @param IsShowMe
+//  */
+// export function apiFutureSearchChartList(params){
+//     return get('/future_good/chart_info/search_by_es',params)
+// }
 
 
-/**
- * 获取商品价格分类数据
- */
-export function apiFutureChartClassifyList(params){
-    return get('/future_good/chart_classify/list',params)
-}
+// /**
+//  * 获取商品价格分类数据
+//  */
+// export function apiFutureChartClassifyList(params){
+//     return get('/future_good/chart_classify/list',params)
+// }
 
 
-/**
- * 商品价格图库图表另存为
- * @param ChartInfoId
- * @param ChartClassifyId
- * @param ChartName
- */
-export function apiFutureChartSaveOther(params){
-    return post('/future_good/chart_info/copy',params)
+// /**
+//  * 商品价格图库图表另存为
+//  * @param ChartInfoId
+//  * @param ChartClassifyId
+//  * @param ChartName
+//  */
+// export function apiFutureChartSaveOther(params){
+//     return post('/future_good/chart_info/copy',params)
+// }
+
+export default{
+    /**
+     * 搜索图表
+     * @param PageSize
+     * @param CurrentIndex
+     * @param KeyWord
+     * @param IsShowMe
+     */
+    searchChartList(params){
+        return get('/future_good/chart_info/search_by_es',params)
+    },
+    /**
+     * 获取商品价格分类数据
+     */
+    chartClassifyList(params){
+        return get('/future_good/chart_classify/list',params)
+    },
+    /**
+     * 商品价格图库图表另存为
+     * @param ChartInfoId
+     * @param ChartClassifyId
+     * @param ChartName
+     */
+    chartSaveOther(params){
+        return post('/future_good/chart_info/copy',params)
+    }
 }
 }

+ 2 - 1
src/api/index.js

@@ -11,7 +11,8 @@ import { showLoadingToast,showToast,closeToast } from "vant";
 
 
 //loading动画白名单
 //loading动画白名单
 const LOADINGWHITELIST=[
 const LOADINGWHITELIST=[
-  '/public/wechat_warning'
+  '/public/wechat_warning',
+  '/report/saveReportContent'
 ]
 ]
 // 请求数
 // 请求数
 let LOADINGCOUNT = 0;
 let LOADINGCOUNT = 0;

+ 15 - 0
src/api/lineEquationChart.js

@@ -0,0 +1,15 @@
+//拟合方程曲线
+import { get,post } from "./index";
+
+export default {
+    /**
+     * 搜索拟合方程曲线图表
+     * @param PageSize
+     * @param CurrentIndex
+     * @param KeyWord
+     * @param IsShowMe
+     */
+    searchChartList(params){
+        return get('/line_equation/chart_info/search_by_es',params)
+    },
+}

+ 109 - 0
src/api/myETA.js

@@ -118,4 +118,113 @@ export function apiChartDel(params){
  */
  */
 export function apiMyETAChartSearch(params){
 export function apiMyETAChartSearch(params){
     return get('/my_chart/search_by_es',params)
     return get('/my_chart/search_by_es',params)
+}
+
+export default{
+    /**
+     * 获取我的图库分类
+     */
+    myClassifyList(){
+        return get('/my_chart/classify/list',{})
+    },
+    /**
+     * 获取公共图库分类
+     */
+    publicClassifyList(){
+        return get('/my_chart/classify/public_list',{})
+    },
+    /**
+     * 新增我的分类
+     * @param MyChartClassifyName
+     */
+    addClassify(params){
+        return post('/my_chart/classify/add',params)
+    },
+    /**
+     * 编辑我的分类
+     * @param MyChartClassifyName
+     * @param MyChartClassifyId
+     */
+    eidtClassify(params){
+        return post('/my_chart/classify/edit',params)
+    },
+    /**
+     * 删除我的分类
+     * @param MyChartClassifyId
+     */
+    deleteClassify(params){
+        return post('/my_chart/classify/delete',params)
+    },
+    /**
+     * 将分类设置共享
+     * @param IsPublic 1共享 0不共享
+     * @param MyChartClassifyId
+     */
+    shareClassify(params){
+        return post('/my_chart/classify/modify_public',params)
+    },
+    /**
+     * 复制公共图库分类
+     * @param MyChartClassifyId 
+    */
+    classifyCopy(params){
+        return post('/my_chart/classify/copy',params)
+    },
+    /**
+     * 获取图库分类下的图列表
+     * @param PageSize
+     * @param CurrentIndex
+     * @param MyChartClassifyId
+     */
+    myChartList(params){
+        return get('/my_chart/list',params)
+    },
+    /**
+     * 将图表从我的图库中移除
+     * @param MyChartClassifyId
+     * @param MyChartId
+     */
+    myChartRemove(params){
+        return post('/my_chart/delete',params)
+    },
+    /**
+     * 将图表加入我的图库分类中
+     * @param ChartInfoId
+     * @param {Array} MyChartClassifyId eg:[462,456]
+     */
+    myChartAdd(params){
+        return post('/my_chart/add',params)
+    },
+    /**
+     * 获取当前的图在我的图库中哪些分类存在
+     * @param ChartInfoId
+     */
+    chartInMyClassifyList(params){
+        return get('/my_chart/get_mychart_classify_id_list',params)
+    },
+    /**
+     * 将图表加入图库分类中
+     * @param ChartInfoId
+     * @param {arr} MyChartClassifyId
+     */
+    chartAddToClassify(params){
+        return post('/my_chart/add_to_classify',params)
+    },
+    /**
+     * 删除图表分类/图表
+     * @param ChartClassifyId
+     * @param ChartInfoId
+     */
+    chartDel(params){
+        return post('/datamanage/chart_classify/delete',params)
+    },
+    /**
+     * myETA搜索我的图库数据
+     * @param Keyword
+     * @param CurrentIndex
+     * @param PageSize
+     */
+    myETAChartSearch(params){
+        return get('/my_chart/search_by_es',params)
+    }
 }
 }

+ 37 - 1
src/api/report.js

@@ -1,7 +1,6 @@
 // 报告模块
 // 报告模块
 import { get,post } from "./index";
 import { get,post } from "./index";
 
 
-
 export default {
 export default {
     /**
     /**
      * 获取报告列表
      * 获取报告列表
@@ -121,5 +120,42 @@ export default {
     // 获取研报作者列表
     // 获取研报作者列表
     reportAuthorList:params=>{
     reportAuthorList:params=>{
         return get('/report/author',params)
         return get('/report/author',params)
+    },
+    /**
+     * 通过报告分类id获取报告内容(继承上一篇报告)
+     * @param ClassifyIdFirst
+     * @param ClassifyIdSecond
+     */
+    reportDetailByClassifyId(params){
+        return get('/report/classifyIdDetail',params)
+    },
+    /**
+     * 新增报告
+     */
+    reportAdd(params){
+        return post('/report/add',params)
+    },
+    /**
+     * 编辑报告
+     */
+    reportEdit(params){
+        return post('/report/edit',params)
+    },
+    /**
+     * 报告mark
+     * @param ReportId
+     * @param Status
+     */
+    reportMark(params){
+        return post('/report/mark',params)
+    },
+    /**
+     * 保存报告内容
+     * @param ReportId
+     * @param Content
+     * @param NoChange 0内容没变 1内容变了
+     */
+    reportContentSave(params){
+        return post('/report/saveReportContent',params)
     }
     }
 }
 }

+ 14 - 0
src/api/sandBox.js

@@ -0,0 +1,14 @@
+//沙盘图
+import { get,post } from "./index";
+
+export default {
+    /**
+     * 获取沙盘图列表
+     * @param CurrentIndex
+     * @param PageSize
+     * @param Keyword
+     */
+    getSandBoxList(params){
+        return get('/sandbox/list_by_quote',params)
+    }
+}

+ 25 - 6
src/api/sheet.js

@@ -2,10 +2,29 @@
 import { get,post } from "./index";
 import { get,post } from "./index";
 
 
 
 
-/**
- * 通过code获取表格详情
- * @param UniqueCode 
- */
-export function apiSheetInfoByCode(params){
-    return get('/datamanage/excel_info/table_data',params)
+// /**
+//  * 通过code获取表格详情
+//  * @param UniqueCode 
+//  */
+// export function apiSheetInfoByCode(params){
+//     return get('/datamanage/excel_info/table_data',params)
+// }
+
+export default {
+    /**
+     * 通过code获取表格详情
+     * @param UniqueCode 
+     */
+    infoByCode(params){
+        return get('/datamanage/excel_info/table_data',params)
+    },
+    /**
+     * 表格列表
+     * @param Keyword
+     * @param CurrentIndex
+     * @param PageSize
+     */
+    sheetList(params){
+        return get('/datamanage/excel_info/list',params)
+    }
 }
 }

+ 58 - 54
src/hooks/useFroalaEditor.js

@@ -1,60 +1,64 @@
 import { ref } from "vue";
 import { ref } from "vue";
 
 
-const options = {
-	toolbarButtons: [
-		"insertImage",
-		"insertVideo",
-		"embedly",
-		"insertFile",
-		"textColor",
-		"bold",
-		"italic",
-		"underline",
-		"strikeThrough",
-		"fontFamily",
-		"fontSize",
-		"color",
-		"lineHeight",
-		"align",
-		"formatOL",
-		"formatUL",
-		"outdent",
-		"indent",
-		"quote",
-		"insertTable",
-		"insertHR",
-		"undo",
-		"redo",
-	],
-	height: 400,
-	fontSize: ["12", "14", "16", "18", "20", "24", "28", "32", "36", "40"],
-	
-	fontSizeDefaultSelection: "16",
-	theme: "dark", //主题
-	placeholderText: "请输入内容",
-	language: "zh_cn", //国际化
-	imageUploadURL: import.meta.env.VITE_APP_API_URL + '/report/uploadImg', //上传url
-	videoUploadURL: import.meta.env.VITE_APP_API_URL + '/report/uploadImg', //上传url
-	fileUploadURL: import.meta.env.VITE_APP_API_URL + '/report/uploadImg', //上传url 更多上传介绍 请访问https://www.froala.com/wysiwyg-editor/docs/options
-	imageDefaultWidth: false,
-	quickInsertButtons: ['image', 'ul', 'ol'], //快速插入项
-	toolbarVisibleWithoutSelection: true, //是否开启 不选中模式
-	toolbarSticky: false, //操作栏是否自动吸顶
-	saveInterval: 0,
-	charCounterCount: false,
-	reportloadding: false,
-	lastsavetime: '',
-	isAddEnter: false, //是否已经添加过
-	timer: null,
-	ischange: false,
-	isPublishloading: false,
-	events:{
-
-	}
-};
-
 export function useInitFroalaEditor() {
 export function useInitFroalaEditor() {
 	let FroalaEditorIns = ref(null);
 	let FroalaEditorIns = ref(null);
+	let frolaEditorContentChange=ref(false)
+
+	const options = {
+		toolbarButtons: [
+			"insertImage",
+			"insertVideo",
+			"embedly",
+			"insertFile",
+			"textColor",
+			"bold",
+			"italic",
+			"underline",
+			"strikeThrough",
+			"fontFamily",
+			"fontSize",
+			"color",
+			"lineHeight",
+			"align",
+			"formatOL",
+			"formatUL",
+			"outdent",
+			"indent",
+			"quote",
+			"insertTable",
+			"insertHR",
+			"undo",
+			"redo",
+		],
+		height: 400,
+		fontSize: ["12", "14", "16", "18", "20", "24", "28", "32", "36", "40"],
+		
+		fontSizeDefaultSelection: "16",
+		theme: "dark", //主题
+		placeholderText: "请输入内容",
+		language: "zh_cn", //国际化
+		imageUploadURL: import.meta.env.VITE_APP_API_URL + '/report/uploadImg', //上传url
+		videoUploadURL: import.meta.env.VITE_APP_API_URL + '/report/uploadImg', //上传url
+		fileUploadURL: import.meta.env.VITE_APP_API_URL + '/report/uploadImg', //上传url 更多上传介绍 请访问https://www.froala.com/wysiwyg-editor/docs/options
+		imageDefaultWidth: false,
+		quickInsertButtons: ['image', 'ul', 'ol'], //快速插入项
+		toolbarVisibleWithoutSelection: true, //是否开启 不选中模式
+		toolbarSticky: false, //操作栏是否自动吸顶
+		saveInterval: 0,
+		charCounterCount: false,
+		reportloadding: false,
+		lastsavetime: '',
+		isAddEnter: false, //是否已经添加过
+		timer: null,
+		ischange: false,
+		isPublishloading: false,
+		events:{
+			contentChanged:function (){
+
+				frolaEditorContentChange.value=true
+			}
+		}
+	};
 
 
 	const initFroalaEditor = (el,opts) => {
 	const initFroalaEditor = (el,opts) => {
 		console.log(opts);
 		console.log(opts);
@@ -65,7 +69,7 @@ export function useInitFroalaEditor() {
 
 
 	return {
 	return {
 		FroalaEditorIns,
 		FroalaEditorIns,
-
+		frolaEditorContentChange,
 		initFroalaEditor,
 		initFroalaEditor,
 	}
 	}
 }
 }

+ 8 - 0
src/router/report.js

@@ -1,5 +1,13 @@
 // 中文研报路由模块
 // 中文研报路由模块
 export const reportRoutes=[
 export const reportRoutes=[
+    {
+        path:"/report/edit",
+        name:"ReportEdit",
+        component: () => import("@/views/report/EditReport.vue"),
+        meta: { 
+            title: "编辑研报",
+        },
+    },
     {
     {
         path:"/report/add",
         path:"/report/add",
         name:"ReportAdd",
         name:"ReportAdd",

+ 6 - 6
src/views/myETA/ChooseChart.vue

@@ -1,8 +1,8 @@
 <script setup>
 <script setup>
 import {reactive, ref} from 'vue'
 import {reactive, ref} from 'vue'
-import {apiPubChartList} from '@/api/chart'
-import {apiFutureSearchChartList} from '@/api/futureChart'
-import {apiCorrelationChartList} from '@/api/correlationChart'
+import apiChart from '@/api/chart'
+import apiFuture from '@/api/futureChart'
+import apiCorrelation from '@/api/correlationChart'
 import {apiMyClassifyList,apiMyChartAdd,apiAddClassify} from '@/api/myETA'
 import {apiMyClassifyList,apiMyChartAdd,apiAddClassify} from '@/api/myETA'
 import { showToast } from 'vant';
 import { showToast } from 'vant';
 import { useWindowSize } from '@vueuse/core'
 import { useWindowSize } from '@vueuse/core'
@@ -45,11 +45,11 @@ async function getChartList(){
     }
     }
     let res
     let res
     if(type.value==1){
     if(type.value==1){
-        res=await apiPubChartList(params)
+        res=await apiChart.pubChartList(params)
     }else if(type.value==2){
     }else if(type.value==2){
-        res=await apiFutureSearchChartList(params)
+        res=await apiFuture.searchChartList(params)
     }else if(type.value==3){
     }else if(type.value==3){
-        res=await apiCorrelationChartList(params)
+        res=await apiCorrelation.chartList(params)
     }
     }
     
     
     if(res.Ret==200){
     if(res.Ret==200){

+ 9 - 9
src/views/myETA/components/ChartSaveOther.vue

@@ -1,8 +1,8 @@
 <script setup>
 <script setup>
 import {watch,ref} from 'vue'
 import {watch,ref} from 'vue'
-import {apiETAChartClassifyList,apiETAChartSaveOther} from '@/api/chart'
-import {apiCorrelationChartClassifyList,apiCorrelationChartSaveOther} from '@/api/correlationChart'
-import {apiFutureChartClassifyList,apiFutureChartSaveOther} from '@/api/futureChart'
+import apiETAChart from '@/api/chart'
+import apiCorrelation from '@/api/correlationChart'
+import apiFuture from '@/api/futureChart'
 import { showToast } from 'vant'
 import { showToast } from 'vant'
 import { useWindowSize } from '@vueuse/core'
 import { useWindowSize } from '@vueuse/core'
 
 
@@ -44,11 +44,11 @@ let opts=ref([])
 async function getOpts(){
 async function getOpts(){
     let res
     let res
     if(props.source==2){
     if(props.source==2){
-        res=await apiFutureChartClassifyList({})
+        res=await apiFuture.chartClassifyList({})
     }else if(props.source==3){
     }else if(props.source==3){
-        res=await apiCorrelationChartClassifyList({})
+        res=await apiCorrelation.chartClassifyList({})
     }else{
     }else{
-        res=await apiETAChartClassifyList({})
+        res=await apiETAChart.ETAChartClassifyList({})
     }
     }
     if(res.Ret!=200) return
     if(res.Ret!=200) return
     const arr=res.Data.AllNodes||[]
     const arr=res.Data.AllNodes||[]
@@ -84,11 +84,11 @@ async function handleConfirmSave(){
     }
     }
     let res
     let res
     if(props.source==2){
     if(props.source==2){
-        res=await apiFutureChartSaveOther(params)
+        res=await apiFuture.chartSaveOther(params)
     }else if(props.source==3){
     }else if(props.source==3){
-        res=await apiCorrelationChartSaveOther(params)
+        res=await apiCorrelation.chartSaveOther(params)
     }else{
     }else{
-        res=await apiETAChartSaveOther(params)
+        res=await apiETAChart.ETAChartSaveOther(params)
     }
     }
 
 
     if(res.Ret!==200) return
     if(res.Ret!==200) return

+ 7 - 5
src/views/ppt/Detail.vue

@@ -95,10 +95,11 @@ function onSelectPlayOpt(e){
         const itemH=pptContentHeight.value/conArr.value.length
         const itemH=pptContentHeight.value/conArr.value.length
         index=Math.floor(y.value/itemH)
         index=Math.floor(y.value/itemH)
     }
     }
-    // 本来可以定义属性close-on-click-action自动关闭弹窗的
-    //不知道为啥加了keepalive 返回时他弹窗又会自动出来 所以加了v-if
-    //并且手动控制收起
-    showPlayOpt.value=false
+    // // 本来可以定义属性close-on-click-action自动关闭弹窗的
+    // //不知道为啥加了keepalive 返回时他弹窗又会自动出来 所以加了v-if
+    // //并且手动控制收起
+    // showPlayOpt.value=false
+    // teleport="body" 可解决
     if(window.location.pathname.startsWith('/ppten')){
     if(window.location.pathname.startsWith('/ppten')){
         router.push({
         router.push({
             path:"/ppten/preview",
             path:"/ppten/preview",
@@ -266,10 +267,11 @@ const showPopover=ref(false)
     </van-dialog>
     </van-dialog>
     <!-- 播放选项弹窗 -->
     <!-- 播放选项弹窗 -->
     <van-action-sheet
     <van-action-sheet
-        v-if="showPlayOpt"
+        teleport="body"
         v-model:show="showPlayOpt" 
         v-model:show="showPlayOpt" 
         :actions="playActions" 
         :actions="playActions" 
         cancel-text="取消"
         cancel-text="取消"
+        close-on-click-action
         @select="onSelectPlayOpt" 
         @select="onSelectPlayOpt" 
     />
     />
 
 

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

@@ -1,6 +1,6 @@
 <script setup>
 <script setup>
 import {computed,onMounted,ref} from 'vue'
 import {computed,onMounted,ref} from 'vue'
-import {apiChartInfoByCode} from '@/api/chart.js'
+import apiChart from '@/api/chart.js'
 import {chartRender} from '@/hooks/chart/render'
 import {chartRender} from '@/hooks/chart/render'
 
 
 const props=defineProps({
 const props=defineProps({
@@ -19,7 +19,7 @@ const renderId=computed(()=>{
 // 获取图表详情
 // 获取图表详情
 let chartIsDelete=ref(false)//图表是否被删除
 let chartIsDelete=ref(false)//图表是否被删除
 async function getChartInfo(){
 async function getChartInfo(){
-    const res=await apiChartInfoByCode({UniqueCode:props.itemData.chartId,IsCache: true})
+    const res=await apiChart.chartInfoByCode({UniqueCode:props.itemData.chartId,IsCache: true})
     if(res.Ret===200){
     if(res.Ret===200){
         if(!res.Data.ChartInfo){
         if(!res.Data.ChartInfo){
             chartIsDelete.value=true
             chartIsDelete.value=true

+ 2 - 2
src/views/ppt/components/SheetWrap.vue

@@ -1,6 +1,6 @@
 <script setup>
 <script setup>
 import {onMounted,ref} from 'vue'
 import {onMounted,ref} from 'vue'
-import {apiSheetInfoByCode} from '@/api/sheet.js'
+import apiSheet from '@/api/sheet.js'
 
 
 const props=defineProps({
 const props=defineProps({
     itemData:{
     itemData:{
@@ -12,7 +12,7 @@ const props=defineProps({
 // 获取表格数据
 // 获取表格数据
 let list=ref([])
 let list=ref([])
 async function getSheetInfo(){
 async function getSheetInfo(){
-    const res=await apiSheetInfoByCode({UniqueCode:props.itemData.sheetId})
+    const res=await apiSheet.infoByCode({UniqueCode:props.itemData.sheetId})
     if(res.Ret===200){
     if(res.Ret===200){
         list.value=res.Data?.TableInfo?.TableDataList||[]
         list.value=res.Data?.TableInfo?.TableDataList||[]
     }
     }

+ 2 - 2
src/views/ppt/hooks/usePPTPublish.js

@@ -1,5 +1,5 @@
 // ppt发布逻辑
 // ppt发布逻辑
-import {apiSheetInfoByCode} from '@/api/sheet.js'
+import apiSheet from '@/api/sheet.js'
 import {apiPPTPublish} from '@/api/ppt.js'
 import {apiPPTPublish} from '@/api/ppt.js'
 import { showToast,showLoadingToast } from 'vant';
 import { showToast,showLoadingToast } from 'vant';
 import pptxgen from "pptxgenjs";
 import pptxgen from "pptxgenjs";
@@ -479,7 +479,7 @@ async function pageToPPT(){
             }else if (elements[j].type==='sheet'){
             }else if (elements[j].type==='sheet'){
                 console.log('table...')
                 console.log('table...')
                 const sheetId = elements[j].sheetId
                 const sheetId = elements[j].sheetId
-                const res=await apiSheetInfoByCode({UniqueCode:sheetId})
+                const res=await apiSheet.infoByCode({UniqueCode:sheetId})
                 if(res.Ret===200){
                 if(res.Ret===200){
                     sheetData = getTableData(res.Data?.TableInfo?.TableDataList||[])
                     sheetData = getTableData(res.Data?.TableInfo?.TableDataList||[])
                 }
                 }

+ 275 - 12
src/views/report/AddReport.vue

@@ -1,8 +1,17 @@
 <script setup name="ReportAdd">
 <script setup name="ReportAdd">
-import {ref,onMounted} from 'vue'
+import {ref,onMounted,onUnmounted} from 'vue'
 import {useInitFroalaEditor} from '@/hooks/useFroalaEditor'
 import {useInitFroalaEditor} from '@/hooks/useFroalaEditor'
 import EditReportBaseInfo from './components/EditReportBaseInfo.vue'
 import EditReportBaseInfo from './components/EditReportBaseInfo.vue'
 import ReportInsertContent from './components/reportInsert/Index.vue'
 import ReportInsertContent from './components/reportInsert/Index.vue'
+import apiReport from '@/api/report'
+import apiChart from '@/api/chart'
+import moment from 'moment'
+import { showToast } from 'vant'
+import { useRouter } from 'vue-router'
+import {useCachedViewsStore} from '@/store/modules/cachedViews'
+const cachedViewsStore=useCachedViewsStore()
+
+const router=useRouter()
 
 
 
 
 const {FroalaEditorIns,initFroalaEditor}=useInitFroalaEditor()
 const {FroalaEditorIns,initFroalaEditor}=useInitFroalaEditor()
@@ -15,17 +24,204 @@ onMounted(() => {
 
 
 // 报告基本内容
 // 报告基本内容
 const showReportBaseInfo=ref(false)
 const showReportBaseInfo=ref(false)
-function handleReportBaseInfoChange(e){
-    console.log(e);
+let reportBaseInfoData={
+    addType:1,
+    classifyName:[],
+    author:['FICC团队'],
+    frequency: ['日度'],
+    createtime:moment().format('YYYY-MM-DD'),
+    title:'',
+    abstract:''
+}
+async function handleReportBaseInfoChange(e){
+    reportBaseInfoData=e
+
+    // 继承报告 覆盖一次
+    if(e.addType===2&&e.classifyName.length===2){
+        const res=await apiReport.reportDetailByClassifyId({
+            ClassifyIdFirst:e.classifyName[0].id,
+            ClassifyIdSecond:e.classifyName[1].id
+        })
+        if(res.Ret===200){
+            if(res.Data===null){
+                showToast('此分类暂无报告')
+            }else{
+                reportBaseInfoData.author=res.Data.Author ? res.Data.Author.split(',') : ['FICC团队']
+                reportBaseInfoData.frequency=[res.Data.Frequency]
+                reportBaseInfoData.createtime=moment().format('YYYY-MM-DD')
+                reportBaseInfoData.title=res.Data.Title
+                reportBaseInfoData.abstract=res.Data.Abstract
+                FroalaEditorIns.value.html.set(res.Data.Content);
+            }
+        }
+    }
+    
     showReportBaseInfo.value=false
     showReportBaseInfo.value=false
 }
 }
 
 
 // 报告插入数据弹窗
 // 报告插入数据弹窗
-const showReportInsertPop=ref(true)
+const showReportInsertPop=ref(false)
+/**
+ * list:[UniqueCode] 图表code
+ * type:iframe/img 插入的为iframe或者图片
+ * chartType: chart-图表,sheet-表格
+ */
+function handleInsert({list,type,chartType}){
+    if(type==='iframe'){
+        let link;
+        if(chartType==='chart'){
+            link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/chartshow':'https://charttest.hzinsights.com/chartshow'
+            list.forEach(item => {
+                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+						<iframe src='${link}?code=${item}&fromPage=' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
+					</p>`,false)
+            });
+        }else if(chartType==='sheet'){
+            link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/sheetshow':'https://charttest.hzinsights.com/sheetshow'
+            list.forEach(item => {
+                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+						<iframe src='${link}?code=${item}' class='iframe${item}'  width='100%' style='border-width:0px;'></iframe>
+					</p>`,false)
+            });
+        }
+    }else if(type==='img'){
+        list.forEach(item=>{
+            FroalaEditorIns.value.html.insert(`<img style='width:100%' src='${item}' />`,false)
+        })
+    }
+
+    showReportInsertPop.value=false
+}
+
+// 更新sheet表格高度
+function reInitSheetIframe(e){
+    const { height,code } = e.data;
+    let iframeDom = document.getElementsByClassName(`iframe${code}`)
+    Array.prototype.forEach.call(iframeDom, function (ele) {
+        ele.height = `${height+45}px`;
+    });
+}
+
+onMounted(()=>{
+    window.addEventListener('message',reInitSheetIframe)
+})
+onUnmounted(()=>{
+    window.removeEventListener('message',reInitSheetIframe)
+})
 
 
+// 刷新所有图表
+async function handleRefreshAllChart(){
+    let code_arr = [];
+    $('iframe').each((k,i) => {
+        try {
+          let href = $(i).attr('src');
+          code_arr.push(href.slice(href.indexOf('code=') + 5));
+        } catch (err) {
+        }
+    });
+    if(!code_arr.length) return showToast('请插入图表');
+    const res=await apiChart.refreshChartMultiple({ChartInfoCode:code_arr})
+    if(res.Ret===200){
+        $('iframe').each((k,i) => {
+          $(i).attr('src',$(i).attr('src'))
+        });
+        showToast('刷新成功')
+    }
+}
 
 
-function handlePublish(){
-    console.log(FroalaEditorIns.value.html.insert('foo bar', true));
+
+// 发布报告-fb;保存-cg;预览-yl
+const showPublishPop=ref(false)
+async function handleReportOpt(type){
+    if(reportBaseInfoData.classifyName.length===0){
+        showToast('请选择报告分类')
+        return
+    }
+    if(!reportBaseInfoData.title){
+        showToast('请填写报告标题')
+        return
+    }
+    const params={
+        AddType: reportBaseInfoData.addType,
+		ClassifyIdFirst: reportBaseInfoData.classifyName[0].id,
+		ClassifyNameFirst: reportBaseInfoData.classifyName[0].text,
+        ClassifyIdSecond: reportBaseInfoData.classifyName[1].id,
+		ClassifyNameSecond: reportBaseInfoData.classifyName[1].text,
+		Title: reportBaseInfoData.title,
+		Abstract: reportBaseInfoData.abstract,
+		Author:reportBaseInfoData.author.join(','),
+		Frequency: reportBaseInfoData.frequency[0],
+		Content: $('.fr-element').html(),
+		CreateTime: reportBaseInfoData.createtime,
+		ReportVersion: 2,
+        State:1
+    }
+    console.log(params);
+    cachedViewsStore.removeCaches('ReportList')
+    if(type==='cg'){
+        // 存草稿
+        const res=await apiReport.reportAdd(params)
+        if(res.Ret===200){
+            showToast('保存成功')
+            router.replace({
+                path:'/report/edit',
+                query:{
+                    id:res.Data.ReportId
+                }
+            })
+        }
+    }
+    if(type==='fb'){
+        // 发布
+        const hasTel=reportBaseInfoData.classifyName[1].HasTeleconference
+        //有电话会的不提示推送客群
+        if(hasTel==1){
+            const res=await apiReport.reportAdd(params)
+            if(res.Ret===200){
+                reportPublish(res.Data.ReportId)
+            }
+        }else{
+            // 显示发布提示弹窗,提示推送客群
+            showPublishPop.value=true
+        }
+    }
+}
+
+// 点击发布提示弹窗中的操作按钮
+async function handleConfirmPublish(e){
+    const params={
+        AddType: reportBaseInfoData.addType,
+		ClassifyIdFirst: reportBaseInfoData.classifyName[0].id,
+		ClassifyNameFirst: reportBaseInfoData.classifyName[0].text,
+        ClassifyIdSecond: reportBaseInfoData.classifyName[1].id,
+		ClassifyNameSecond: reportBaseInfoData.classifyName[1].text,
+		Title: reportBaseInfoData.title,
+		Abstract: reportBaseInfoData.abstract,
+		Author:reportBaseInfoData.author.join(','),
+		Frequency: reportBaseInfoData.frequency[0],
+		Content: $('.fr-element').html(),
+		CreateTime: reportBaseInfoData.createtime,
+		ReportVersion: 2,
+        State:1
+    }
+    const saveRes=await apiReport.reportAdd(params)
+    if(e===1){//仅发布
+        reportPublish(saveRes.Data.ReportId)
+    }else if(e===2){
+        const pubRes=await apiReport.reportPublish({ReportIds:saveRes.Data.ReportId.toString()})
+        if(pubRes.Ret!==200) return
+        const msgRes=await apiReport.reportMessageSend({ReportId:saveRes.Data.ReportId})
+        if(msgRes.Ret!==200) return
+        router.back()
+    }
+}
+
+async function reportPublish(id){
+    const res=await apiReport.reportPublish({ReportIds:id.toString()})
+    if(res.Ret===200){
+        console.log('back');
+        router.back()
+    }
 }
 }
 
 
 </script>
 </script>
@@ -39,24 +235,24 @@ function handlePublish(){
         <!-- 底部操作 -->
         <!-- 底部操作 -->
         <div class="bot-action-box">
         <div class="bot-action-box">
             <div class="left-box">
             <div class="left-box">
-                <div class="item">
+                <div class="item" @click="handleRefreshAllChart">
                     <img src="@/assets/imgs/report/icon_refresh.png" alt="">
                     <img src="@/assets/imgs/report/icon_refresh.png" alt="">
                     <span>刷新</span>
                     <span>刷新</span>
                 </div>
                 </div>
-                <div class="item">
+                <div class="item" @click="handleReportOpt('yl')">
                     <img src="@/assets/imgs/report/icon_preview.png" alt="">
                     <img src="@/assets/imgs/report/icon_preview.png" alt="">
                     <span>预览</span>
                     <span>预览</span>
                 </div>
                 </div>
-                <div class="item">
+                <div class="item" @click="handleReportOpt('cg')">
                     <img src="@/assets/imgs/report/icon_save2.png" alt="">
                     <img src="@/assets/imgs/report/icon_save2.png" alt="">
                     <span>保存</span>
                     <span>保存</span>
                 </div>
                 </div>
-                <div class="item" @click="handlePublish">
+                <div class="item" @click="handleReportOpt('fb')">
                     <img src="@/assets/imgs/report/icon_publish3.png" alt="">
                     <img src="@/assets/imgs/report/icon_publish3.png" alt="">
                     <span>发布</span>
                     <span>发布</span>
                 </div>
                 </div>
             </div>
             </div>
-            <div class="right-btn">
+            <div class="right-btn" @click="showReportInsertPop=true">
                 <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
                 <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
                     <path d="M12.0499 15.9499V27.5H15.9499V15.9499H27.5V12.0499H15.9499V0.5H12.0499V12.0499H0.5V15.9499H12.0499Z" fill="white"/>
                     <path d="M12.0499 15.9499V27.5H15.9499V15.9499H27.5V12.0499H15.9499V0.5H12.0499V12.0499H0.5V15.9499H12.0499Z" fill="white"/>
                 </svg>
                 </svg>
@@ -79,10 +275,77 @@ function handlePublish(){
         position="bottom"
         position="bottom"
         round
         round
     >
     >
-        <report-insert-content/>
+        <report-insert-content v-if="showReportInsertPop" @insert="handleInsert"/>
+    </van-popup>
+
+    <!-- 发布提示 -->
+    <van-popup
+        v-model:show="showPublishPop"
+    >
+        <div class="publish-report-pop-box">
+            <div class="title">发布提示</div>
+            <p class="tips">是否发布报告,且推送模板消息和客户群?</p>
+            <div class="btns">
+                <div class="btn blue" @click="handleConfirmPublish(2)">发布&推送</div>
+                <div class="btn" @click="handleConfirmPublish(1)">仅发布</div>
+                <div class="btn" @click="showPublishPop=false">取消发布</div>
+            </div>
+        </div>
     </van-popup>
     </van-popup>
 </template>
 </template>
 <style lang="scss" scoped>
 <style lang="scss" scoped>
+.publish-report-pop-box{
+    padding: 48px;
+    .title{
+        font-size: 36px;
+        text-align: center;
+        margin-bottom: 32px;
+    }
+    .tips{
+        color: $font-grey;
+        margin-bottom: 48px;
+    }
+    .btns{
+        .btn{
+            line-height: 96px;
+            border-radius: 12px;
+            text-align: center;
+            font-size: 32px;
+            font-weight: 600;
+            margin-bottom: 24px;
+            background-color: #F2F3FF;
+            color: $theme-color;
+        }
+        .blue{
+            background-color: $theme-color;
+            color: #fff;
+        }
+        .disabled{
+            background-color: #a8b0fc;
+        }
+    }
+}
+
+@media screen and (min-width:$media-width){
+    .publish-report-pop-box{
+        padding: 24px;
+        .title{
+            font-size: 18px;
+            margin-bottom: 16px;
+        }
+        .tips{
+            margin-bottom: 24px;
+        }
+        .btns{
+            .btn{
+                line-height: 48px;
+                border-radius: 12px;
+                font-size: 16px;
+                margin-bottom: 12px;
+            }
+        }
+    }
+}
 .add-report-page{
 .add-report-page{
     height: 100vh;
     height: 100vh;
     display: flex;
     display: flex;

+ 480 - 0
src/views/report/EditReport.vue

@@ -0,0 +1,480 @@
+<script setup name="ReportEdit">
+import {ref,onMounted,onUnmounted} from 'vue'
+import {useInitFroalaEditor} from '@/hooks/useFroalaEditor'
+import EditReportBaseInfo from './components/EditReportBaseInfo.vue'
+import ReportInsertContent from './components/reportInsert/Index.vue'
+import apiReport from '@/api/report'
+import apiChart from '@/api/chart'
+import moment from 'moment'
+import { showToast } from 'vant'
+import { useRoute, useRouter } from 'vue-router'
+
+const router=useRouter()
+const route=useRoute()
+
+
+const {FroalaEditorIns,initFroalaEditor,frolaEditorContentChange}=useInitFroalaEditor()
+
+let autoSaveTimer=null
+
+onMounted(() => {
+    const el=document.getElementById('editor')
+    initFroalaEditor('#editor',{height:el.offsetHeight-150})
+    getReportDetail()
+    autoSaveTimer=setInterval(() => {
+        autoSaveReportContent()
+    }, 6000);
+})
+onUnmounted(()=>{
+    clearInterval(autoSaveTimer)
+})
+
+// 自动保存报告
+async function autoSaveReportContent(){
+    const res=await apiReport.reportContentSave({
+        ReportId:Number(route.query.id),
+        Content:$('.fr-element').html(),
+        NoChange:frolaEditorContentChange.value?0:1
+    })
+    frolaEditorContentChange.value=false
+}
+
+// 获取报告详情
+const reportData=ref(null)
+async function getReportDetail(){
+    const res=await apiReport.getReportDetail({
+        ReportId:Number(route.query.id)
+    })
+    if(res.Ret===200){
+        reportData.value=res.Data
+        reportBaseInfoData.addType=res.Data.AddType
+        reportBaseInfoData.classifyName=[
+            {
+                id:res.Data.ClassifyIdFirst,
+                text:res.Data.ClassifyNameFirst,
+                HasTeleconference:0
+            },
+            {
+                id:res.Data.ClassifyIdSecond,
+                text:res.Data.ClassifyNameSecond,
+                HasTeleconference:0
+            }
+        ]
+        reportBaseInfoData.author=res.Data.Author ? res.Data.Author.split(',') : ['FICC团队']
+        reportBaseInfoData.frequency=[res.Data.Frequency]
+        reportBaseInfoData.createtime=moment(res.Data.CreateTime).format('YYYY-MM-DD')
+        reportBaseInfoData.title=res.Data.Title
+        reportBaseInfoData.abstract=res.Data.Abstract
+
+        FroalaEditorIns.value.html.set(res.Data.Content);
+
+        // 查找选中的分类是否有电话会
+        const classifyRes=await apiReport.getClassifyList({
+            CurrentIndex:1,
+            PageSize:1000,
+            KeyWord:'',
+            HideDayWeek:1
+        })
+        if(classifyRes.Ret===200){
+            const arr=classifyRes.Data.List||[]
+            arr.forEach(item=>{
+                if(item.Id==reportBaseInfoData.classifyName[0].id){
+                    reportBaseInfoData.classifyName[0].HasTeleconference=item.HasTeleconference
+                }
+                item.Child&&item.Child.forEach(_item=>{
+                    if(_item.Id==reportBaseInfoData.classifyName[1].id){
+                        reportBaseInfoData.classifyName[1].HasTeleconference=_item.HasTeleconference
+                    }
+                })
+            })
+        }
+
+    }
+}
+
+
+// 报告基本内容
+const showReportBaseInfo=ref(false)
+let reportBaseInfoData={
+    addType:1,
+    classifyName:[],
+    author:['FICC团队'],
+    frequency: ['日度'],
+    createtime:moment().format('YYYY-MM-DD'),
+    title:'',
+    abstract:''
+}
+async function handleReportBaseInfoChange(e){
+    reportBaseInfoData=e
+
+    //继承报告 覆盖一次
+    if(e.addType===2&&e.classifyName.length===2){
+        const res=await apiReport.reportDetailByClassifyId({
+            ClassifyIdFirst:e.classifyName[0].id,
+            ClassifyIdSecond:e.classifyName[1].id
+        })
+        if(res.Ret===200){
+            if(res.Data===null){
+                showToast('此分类暂无报告')
+            }else{
+                reportBaseInfoData.author=res.Data.Author ? res.Data.Author.split(',') : ['FICC团队']
+                reportBaseInfoData.frequency=[res.Data.Frequency]
+                reportBaseInfoData.createtime=moment().format('YYYY-MM-DD')
+                reportBaseInfoData.title=res.Data.Title
+                reportBaseInfoData.abstract=res.Data.Abstract
+                FroalaEditorIns.value.html.set(res.Data.Content);
+            }
+        }
+    }
+    
+    showReportBaseInfo.value=false
+}
+
+// 报告插入数据弹窗
+const showReportInsertPop=ref(false)
+/**
+ * list:[UniqueCode] 图表code
+ * type:iframe/img 插入的为iframe或者图片
+ * chartType: chart-图表,sheet-表格
+ */
+function handleInsert({list,type,chartType}){
+    if(type==='iframe'){
+        let link;
+        if(chartType==='chart'){
+            link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/chartshow':'https://charttest.hzinsights.com/chartshow'
+            list.forEach(item => {
+                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+						<iframe src='${link}?code=${item}&fromPage=' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
+					</p>`,false)
+            });
+        }else if(chartType==='sheet'){
+            link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/sheetshow':'https://charttest.hzinsights.com/sheetshow'
+            list.forEach(item => {
+                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+						<iframe src='${link}?code=${item}' class='iframe${item}'  width='100%' style='border-width:0px;'></iframe>
+					</p>`,false)
+            });
+        }
+    }else if(type==='img'){
+        list.forEach(item=>{
+            FroalaEditorIns.value.html.insert(`<img style='width:100%' src='${item}' />`,false)
+        })
+    }
+
+    showReportInsertPop.value=false
+}
+
+// 更新sheet表格高度
+function reInitSheetIframe(e){
+    const { height,code } = e.data;
+    let iframeDom = document.getElementsByClassName(`iframe${code}`)
+    Array.prototype.forEach.call(iframeDom, function (ele) {
+        ele.height = `${height+45}px`;
+    });
+}
+
+onMounted(()=>{
+    window.addEventListener('message',reInitSheetIframe)
+})
+onUnmounted(()=>{
+    window.removeEventListener('message',reInitSheetIframe)
+})
+
+// 刷新所有图表
+async function handleRefreshAllChart(){
+    let code_arr = [];
+    $('iframe').each((k,i) => {
+        try {
+          let href = $(i).attr('src');
+          code_arr.push(href.slice(href.indexOf('code=') + 5));
+        } catch (err) {
+        }
+    });
+    if(!code_arr.length) return showToast('请插入图表');
+    const res=await apiChart.refreshChartMultiple({ChartInfoCode:code_arr})
+    if(res.Ret===200){
+        $('iframe').each((k,i) => {
+          $(i).attr('src',$(i).attr('src'))
+        });
+        showToast('刷新成功')
+    }
+}
+
+
+// 发布报告-fb;保存-cg;预览-yl
+const showPublishPop=ref(false)
+async function handleReportOpt(type){
+    if(reportBaseInfoData.classifyName.length===0){
+        showToast('请选择报告分类')
+        return
+    }
+    if(!reportBaseInfoData.title){
+        showToast('请填写报告标题')
+        return
+    }
+    const params={
+        ReportId:Number(route.query.id),
+        AddType: reportBaseInfoData.addType,
+		ClassifyIdFirst: reportBaseInfoData.classifyName[0].id,
+		ClassifyNameFirst: reportBaseInfoData.classifyName[0].text,
+        ClassifyIdSecond: reportBaseInfoData.classifyName[1].id,
+		ClassifyNameSecond: reportBaseInfoData.classifyName[1].text,
+		Title: reportBaseInfoData.title,
+		Abstract: reportBaseInfoData.abstract,
+		Author:reportBaseInfoData.author.join(','),
+		Frequency: reportBaseInfoData.frequency[0],
+		Content: $('.fr-element').html(),
+		CreateTime: reportBaseInfoData.createtime,
+		ReportVersion: 2,
+        State:1
+    }
+    console.log(params);
+    cachedViewsStore.removeCaches('ReportList')
+    if(type==='cg'){
+        // 存草稿
+        const res=await apiReport.reportEdit(params)
+        if(res.Ret===200){
+            showToast('保存成功')
+        }
+    }
+    if(type==='fb'){
+        // 发布
+        const hasTel=reportBaseInfoData.classifyName[1].HasTeleconference
+        //有电话会的不提示推送客群
+        if(hasTel==1){
+            const res=await apiReport.reportEdit(params)
+            if(res.Ret===200){
+                reportPublish(res.Data.ReportId)
+            }
+        }else{
+            // 显示发布提示弹窗,提示推送客群
+            showPublishPop.value=true
+        }
+    }
+}
+
+// 点击发布提示弹窗中的操作按钮
+async function handleConfirmPublish(e){
+    const params={
+        ReportId:Number(route.query.id),
+        AddType: reportBaseInfoData.addType,
+		ClassifyIdFirst: reportBaseInfoData.classifyName[0].id,
+		ClassifyNameFirst: reportBaseInfoData.classifyName[0].text,
+        ClassifyIdSecond: reportBaseInfoData.classifyName[1].id,
+		ClassifyNameSecond: reportBaseInfoData.classifyName[1].text,
+		Title: reportBaseInfoData.title,
+		Abstract: reportBaseInfoData.abstract,
+		Author:reportBaseInfoData.author.join(','),
+		Frequency: reportBaseInfoData.frequency[0],
+		Content: $('.fr-element').html(),
+		CreateTime: reportBaseInfoData.createtime,
+		ReportVersion: 2,
+        State:1
+    }
+    const saveRes=await apiReport.reportEdit(params)
+    if(e===1){//仅发布
+        reportPublish(saveRes.Data.ReportId)
+    }else if(e===2){
+        if(reportData.value.MsgIsSend===1) return
+        const pubRes=await apiReport.reportPublish({ReportIds:saveRes.Data.ReportId.toString()})
+        if(pubRes.Ret!==200) return
+        const msgRes=await apiReport.reportMessageSend({ReportId:saveRes.Data.ReportId})
+        if(msgRes.Ret!==200) return
+        router.back()
+    }
+}
+
+async function reportPublish(id){
+    const res=await apiReport.reportPublish({ReportIds:id.toString()})
+    if(res.Ret===200){
+        console.log('back');
+        router.back()
+    }
+}
+
+</script>
+
+<template>
+    <div class="add-report-page">
+        <van-cell title="基础信息" is-link @click="showReportBaseInfo=true"/>
+        <div class="main-wrap">
+            <div class="editor-box" id="editor"></div>
+        </div>
+        <!-- 底部操作 -->
+        <div class="bot-action-box">
+            <div class="left-box">
+                <div class="item" @click="handleRefreshAllChart">
+                    <img src="@/assets/imgs/report/icon_refresh.png" alt="">
+                    <span>刷新</span>
+                </div>
+                <div class="item" @click="handleReportOpt('yl')">
+                    <img src="@/assets/imgs/report/icon_preview.png" alt="">
+                    <span>预览</span>
+                </div>
+                <div class="item" @click="handleReportOpt('cg')">
+                    <img src="@/assets/imgs/report/icon_save2.png" alt="">
+                    <span>保存</span>
+                </div>
+                <div class="item" @click="handleReportOpt('fb')">
+                    <img src="@/assets/imgs/report/icon_publish3.png" alt="">
+                    <span>发布</span>
+                </div>
+            </div>
+            <div class="right-btn" @click="showReportInsertPop=true">
+                <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+                    <path d="M12.0499 15.9499V27.5H15.9499V15.9499H27.5V12.0499H15.9499V0.5H12.0499V12.0499H0.5V15.9499H12.0499Z" fill="white"/>
+                </svg>
+            </div>
+        </div>
+    </div>
+
+    <!-- 报告基础信息 -->
+    <van-popup
+        v-model:show="showReportBaseInfo"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <EditReportBaseInfo v-if="showReportBaseInfo" :defaultData="reportBaseInfoData" @close="showReportBaseInfo=false" @confirm="handleReportBaseInfoChange"/>
+    </van-popup>
+
+    <!-- 报告插入数据模块 -->
+    <van-popup
+        v-model:show="showReportInsertPop"
+        position="bottom"
+        round
+    >
+        <report-insert-content v-if="showReportInsertPop" @insert="handleInsert"/>
+    </van-popup>
+
+    <!-- 发布提示 -->
+    <van-popup
+        v-model:show="showPublishPop"
+    >
+        <div class="publish-report-pop-box">
+            <div class="title">发布提示</div>
+            <p class="tips">是否发布报告,且推送模板消息和客户群?</p>
+            <div class="btns">
+                <div :class="['btn blue',reportData.MsgIsSend===1?'disabled':'']" @click="handleConfirmPublish(2)">发布&推送</div>
+                <div class="btn" @click="handleConfirmPublish(1)">仅发布</div>
+                <div class="btn" @click="showPublishPop=false">取消发布</div>
+            </div>
+        </div>
+    </van-popup>
+</template>
+<style lang="scss" scoped>
+.publish-report-pop-box{
+    padding: 48px;
+    .title{
+        font-size: 36px;
+        text-align: center;
+        margin-bottom: 32px;
+    }
+    .tips{
+        color: $font-grey;
+        margin-bottom: 48px;
+    }
+    .btns{
+        .btn{
+            line-height: 96px;
+            border-radius: 12px;
+            text-align: center;
+            font-size: 32px;
+            font-weight: 600;
+            margin-bottom: 24px;
+            background-color: #F2F3FF;
+            color: $theme-color;
+        }
+        .blue{
+            background-color: $theme-color;
+            color: #fff;
+        }
+        .disabled{
+            background-color: #a8b0fc;
+        }
+    }
+}
+
+@media screen and (min-width:$media-width){
+    .publish-report-pop-box{
+        padding: 24px;
+        .title{
+            font-size: 18px;
+            margin-bottom: 16px;
+        }
+        .tips{
+            margin-bottom: 24px;
+        }
+        .btns{
+            .btn{
+                line-height: 48px;
+                border-radius: 12px;
+                font-size: 16px;
+                margin-bottom: 12px;
+            }
+        }
+    }
+}
+.add-report-page{
+    height: 100vh;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+}
+.van-cell{
+    flex-shrink: 0;
+}
+.main-wrap{
+    flex: 1;
+    width: calc(100% - 32PX);
+    margin: 0 auto;
+    margin-top: 30px;
+    .editor-box{
+        width: 100%;
+        height: 100%;
+    }
+}
+.bot-action-box{
+    padding: 20px 16PX;
+    display: flex;
+    align-items: center;
+    .left-box{
+        flex: 1;
+        background: #FFFFFF;
+        box-shadow: 0px 12px 60px 10px rgba(0, 0, 0, 0.05), 0px 32px 48px 4px rgba(0, 0, 0, 0.04), 0px 16px 20px -10px rgba(0, 0, 0, 0.08);
+        border-radius: 100px;
+        height: 112px;
+        display: flex;
+        align-items: center;
+        margin-right: 20px;
+        padding: 0 20px;
+        .item{
+            flex: 1;
+            text-align: center;
+            font-size: 20px;
+            img{
+                width: 40px;
+                height: 40px;
+                display: block;
+                margin: 5px auto;
+            }
+        }
+    }
+    .right-btn{
+        flex-shrink: 0;
+        position: relative;
+        width: 96px;
+        height: 96px;
+        background-color: $theme-color;
+        border-radius: 50%;
+        box-shadow: 0px 6px 28px 4px rgba(0, 0, 0, 0.05), 0px 16px 20px 2px rgba(0, 0, 0, 0.06), 0px 10px 10px -6px rgba(0, 0, 0, 0.1);
+        svg{
+            width: 27px;
+            height: 27px;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%,-50%);
+        }
+    }
+}
+</style>

+ 58 - 2
src/views/report/List.vue

@@ -187,7 +187,12 @@ function goDetail(item){
 // 长按弹出操作
 // 长按弹出操作
 let activeItem=ref(null)
 let activeItem=ref(null)
 const showReportItemOpt=ref(false)
 const showReportItemOpt=ref(false)
-function onLongPressItem(e){
+async function onLongPressItem(e){
+    // 校验是否有人在编辑报告
+    if(!e.CanEdit){
+        showToast(`${e.Editor}正在编辑中,不可操作!`)
+        return
+    }
     activeItem.value=e
     activeItem.value=e
     showReportItemOpt.value=true
     showReportItemOpt.value=true
 }
 }
@@ -284,6 +289,53 @@ function handleSelectAddReportType(e){
     }else{
     }else{
 
 
     }
     }
+
+}
+
+//点击编辑
+async function handleReportEdit(e){
+    console.log(e);
+    showReportItemOpt.value=false
+    // 先mark一下
+    const markRes=await apiReport.reportMark({
+        Status:1,
+        ReportId:e.Id
+    })
+    if(markRes.Ret===200){
+        if(markRes.Data.Status===1){
+            showToast(markRes.Data.Msg || '该研报正在编辑,不可重复编辑')
+            listState.list.forEach(item=>{
+                if(item.Id===e.Id){
+                    item.CanEdit=false
+                    item.Editor=markRes.Data.Editor || ''
+                }
+            })
+            return
+        }else if(markRes.Data.Status===0){
+            listState.list.forEach(item=>{
+                if(item.Id===e.Id){
+                    item.CanEdit=true
+                    item.Editor=markRes.Data.Editor || ''
+                }
+            })
+        }
+    }else{
+        showToast(markRes.ErrMsg || '未知错误,请稍后重试')
+        return
+    }
+    // 晨报周报
+    if(['day','week'].includes(e.ChapterType)){
+
+        return
+    }
+
+    // 研报
+    router.push({
+        path:'/report/edit',
+        query:{
+            id:e.Id
+        }
+    })
 }
 }
 
 
 </script>
 </script>
@@ -406,7 +458,8 @@ function handleSelectAddReportType(e){
     </div>
     </div>
 
 
     <!-- 新增报告弹窗 -->
     <!-- 新增报告弹窗 -->
-    <van-action-sheet 
+    <van-action-sheet
+        teleport="body"
         v-model:show="showAddReportPop"
         v-model:show="showAddReportPop"
         cancel-text="取消"
         cancel-text="取消"
         close-on-click-action
         close-on-click-action
@@ -424,12 +477,15 @@ function handleSelectAddReportType(e){
 
 
     <!-- 报告item操作 -->
     <!-- 报告item操作 -->
     <van-action-sheet 
     <van-action-sheet 
+        teleport="body"
         v-model:show="showReportItemOpt"
         v-model:show="showReportItemOpt"
         cancel-text="取消"
         cancel-text="取消"
+        close-on-click-action
     >
     >
         <div class="report-item-action-box" v-if="activeItem">
         <div class="report-item-action-box" v-if="activeItem">
             <div class="title">{{activeItem.Title}}</div>
             <div class="title">{{activeItem.Title}}</div>
             <template v-if="activeItem.State==1">
             <template v-if="activeItem.State==1">
+                <div class="item" style="color:#0052D9" @click="handleReportEdit(activeItem)">编辑</div>
                 <div class="item" style="color:#C54322" @click="handleReportDel(activeItem)">删除</div>
                 <div class="item" style="color:#C54322" @click="handleReportDel(activeItem)">删除</div>
                 <div class="item" style="color:#0052D9" @click="handleReportPublish(activeItem)">发布</div>
                 <div class="item" style="color:#0052D9" @click="handleReportPublish(activeItem)">发布</div>
             </template>
             </template>

+ 45 - 8
src/views/report/components/EditReportBaseInfo.vue

@@ -5,13 +5,26 @@ import ListClassify from './ListClassify.vue'
 import apiReport from '@/api/report'
 import apiReport from '@/api/report'
 import {reportFrequencyOpts} from '../utils/config'
 import {reportFrequencyOpts} from '../utils/config'
 import { showToast } from "vant"
 import { showToast } from "vant"
-import {useEidtReportBaseInfo} from '../hooks/useEidtReportBaseInfo'
+import { useRoute } from "vue-router"
 
 
+const route=useRoute()
+
+const props=defineProps({
+    defaultData:null
+})
 
 
 const emits=defineEmits(['close','confirm'])
 const emits=defineEmits(['close','confirm'])
 
 
 // 基本数据
 // 基本数据
-const {reportBaseInfo} =useEidtReportBaseInfo()
+const reportBaseInfo=reactive({
+    addType:props.defaultData?props.defaultData.addType:1,
+    classifyName:props.defaultData?props.defaultData.classifyName:[],
+    author:props.defaultData?props.defaultData.author:['FICC团队'],
+    frequency: props.defaultData?props.defaultData.frequency:['日度'],
+    createtime:props.defaultData?props.defaultData.createtime:moment().format('YYYY-MM-DD'),
+    title:props.defaultData?props.defaultData.title:'',
+    abstract:props.defaultData?props.defaultData.abstract:''
+})
 
 
 // 报告新增类型
 // 报告新增类型
 const showAddTypePop=ref(false)
 const showAddTypePop=ref(false)
@@ -25,6 +38,11 @@ const addTypeOpts=[
         name:'继承报告'
         name:'继承报告'
     }
     }
 ]
 ]
+function handleShowAddType(){
+    // 编辑时不可操作
+    if(route.path=='/report/edit') return
+    showAddTypePop.value=true
+}
 function getAddTypeName(value){
 function getAddTypeName(value){
     return addTypeOpts.filter(item=>item.value===value)[0].name
     return addTypeOpts.filter(item=>item.value===value)[0].name
 }
 }
@@ -40,12 +58,16 @@ const setClassifyVal=computed(()=>{
     }
     }
     return '请选择分类'
     return '请选择分类'
 })
 })
+function handleShowClassify(){
+    showClassifyPop.value=true
+}
 function handleConfirmClassify({firstClassify,secondClassify}){
 function handleConfirmClassify({firstClassify,secondClassify}){
     if(!firstClassify.id||!secondClassify.id){
     if(!firstClassify.id||!secondClassify.id){
         showToast('请选择分类')
         showToast('请选择分类')
         return
         return
     }
     }
     reportBaseInfo.classifyName=[firstClassify,secondClassify]
     reportBaseInfo.classifyName=[firstClassify,secondClassify]
+    reportBaseInfo.title=secondClassify.text
     showClassifyPop.value=false
     showClassifyPop.value=false
 }
 }
 
 
@@ -73,13 +95,23 @@ getAuthorOpts()
 // 报告频度
 // 报告频度
 const showFrequencyPop=ref(false)
 const showFrequencyPop=ref(false)
 const temFrequencyVal=ref(['日度'])
 const temFrequencyVal=ref(['日度'])
+function handleShowFrequency(){
+    temFrequencyVal.value=reportBaseInfo.frequency
+    showFrequencyPop.value=true
+}
 function handleConfirmFrequency(){
 function handleConfirmFrequency(){
     reportBaseInfo.frequency=temFrequencyVal.value
     reportBaseInfo.frequency=temFrequencyVal.value
     showFrequencyPop.value=false
     showFrequencyPop.value=false
 }
 }
 
 
 // 创建日期
 // 创建日期
+const minDate=new Date(2015, 0, 1)
+const defaultDate=ref(new Date())
 const showCreateTimePop=ref(false)
 const showCreateTimePop=ref(false)
+function handleShowCreatetime(){
+    defaultDate.value=new Date(reportBaseInfo.createtime.replace(/-/g,'/'))
+    showCreateTimePop.value=true
+}
 function handleConfirmCreatime(e){
 function handleConfirmCreatime(e){
     reportBaseInfo.createtime=moment(e).format('YYYY-MM-DD')
     reportBaseInfo.createtime=moment(e).format('YYYY-MM-DD')
     showCreateTimePop.value=false
     showCreateTimePop.value=false
@@ -123,11 +155,11 @@ function handleSave(){
 <template>
 <template>
     <div class="report-baseinfo-wrap">
     <div class="report-baseinfo-wrap">
         <van-cell-group>
         <van-cell-group>
-            <van-cell value-class="cell-con" required title="新增方式" :value="getAddTypeName(reportBaseInfo.addType)" is-link @click="showAddTypePop=true"/>
-            <van-cell value-class="cell-con" required title="分类" :value="setClassifyVal" is-link @click="showClassifyPop=true"/>
+            <van-cell value-class="cell-con" required title="新增方式" :value="getAddTypeName(reportBaseInfo.addType)" :is-link="route.path!='/report/edit'" @click="handleShowAddType"/>
+            <van-cell value-class="cell-con" required title="分类" :value="setClassifyVal" is-link @click="handleShowClassify"/>
             <van-cell value-class="cell-con" title="作者" :value="reportBaseInfo.author.join(',')" is-link @click="handleShowSelectAuthor"/>
             <van-cell value-class="cell-con" title="作者" :value="reportBaseInfo.author.join(',')" is-link @click="handleShowSelectAuthor"/>
-            <van-cell value-class="cell-con" title="频度" :value="reportBaseInfo.frequency.join('')" is-link @click="showFrequencyPop=true"/>
-            <van-cell value-class="cell-con" title="创建时间" :value="reportBaseInfo.createtime" is-link @click="showCreateTimePop=true"/>
+            <van-cell value-class="cell-con" title="频度" :value="reportBaseInfo.frequency.join('')" is-link @click="handleShowFrequency"/>
+            <van-cell value-class="cell-con" title="创建时间" :value="reportBaseInfo.createtime" is-link @click="handleShowCreatetime"/>
         </van-cell-group>
         </van-cell-group>
 
 
         <van-cell-group style="margin:10px 0">
         <van-cell-group style="margin:10px 0">
@@ -160,7 +192,10 @@ function handleSave(){
         position="bottom"
         position="bottom"
         round
         round
     >
     >
-        <ListClassify 
+        <ListClassify
+            v-if="showClassifyPop"
+            :defaultVal="reportBaseInfo.classifyName"
+            :noReset="true"
             :hideDayWeek="1" 
             :hideDayWeek="1" 
             :firstClassifyDisabled="true" 
             :firstClassifyDisabled="true" 
             @close="showClassifyPop=false" 
             @close="showClassifyPop=false" 
@@ -205,7 +240,9 @@ function handleSave(){
     </van-popup>
     </van-popup>
 
 
     <!-- 创建日期 -->
     <!-- 创建日期 -->
-    <van-calendar 
+    <van-calendar
+        :min-date="minDate"
+        :default-date="defaultDate"
         v-model:show="showCreateTimePop"
         v-model:show="showCreateTimePop"
         title="选择创建日期"
         title="选择创建日期"
         @confirm="handleConfirmCreatime" 
         @confirm="handleConfirmCreatime" 

+ 20 - 5
src/views/report/components/ListClassify.vue

@@ -3,6 +3,8 @@ import {ref} from 'vue'
 import apiReport from '@/api/report'
 import apiReport from '@/api/report'
 
 
 const props=defineProps({
 const props=defineProps({
+    defaultVal:null,
+    noReset:false,
     hideDayWeek:2,
     hideDayWeek:2,
     firstClassifyDisabled:false,//一级分类是否要判断禁用
     firstClassifyDisabled:false,//一级分类是否要判断禁用
 })
 })
@@ -23,16 +25,26 @@ function getClassifyList(){
                 const child=item.Child?item.Child.map(e=>{
                 const child=item.Child?item.Child.map(e=>{
                     return {
                     return {
                         text:e.ClassifyName,
                         text:e.ClassifyName,
-                        id:e.Id
+                        id:e.Id,
+                        HasTeleconference:e.HasTeleconference
                     }
                     }
                 }):[]
                 }):[]
                 return {
                 return {
                     text:item.ClassifyName,
                     text:item.ClassifyName,
                     id:item.Id,
                     id:item.Id,
                     children:child,
                     children:child,
-                    disabled:props.firstClassifyDisabled&&child.length===0?true:false
+                    disabled:props.firstClassifyDisabled&&child.length===0?true:false,
+                    HasTeleconference:item.HasTeleconference
                 }
                 }
             })
             })
+            if(props.defaultVal&&props.defaultVal[1]){
+                activeId.value=props.defaultVal[1].id
+                list.value.forEach((item,index)=>{
+                    if(item.id===props.defaultVal[0].id){
+                        activeIndex.value=index
+                    }
+                })
+            }
         }
         }
     })
     })
 }
 }
@@ -54,14 +66,16 @@ function handleReset(){
 function handleConfirm(){
 function handleConfirm(){
     const firstClassify={
     const firstClassify={
         text:list.value[activeIndex.value].text,
         text:list.value[activeIndex.value].text,
-        id:list.value[activeIndex.value].id
+        id:list.value[activeIndex.value].id,
+        HasTeleconference:list.value[activeIndex.value].HasTeleconference,
     }
     }
-    let secondClassify={text:'',id:''}
+    let secondClassify={text:'',id:'',HasTeleconference:''}
     if(activeId.value){
     if(activeId.value){
         list.value[activeIndex.value].children.forEach(e => {
         list.value[activeIndex.value].children.forEach(e => {
             if(e.id===activeId.value){
             if(e.id===activeId.value){
                 secondClassify.text=e.text
                 secondClassify.text=e.text
                 secondClassify.id=e.id
                 secondClassify.id=e.id
+                secondClassify.HasTeleconference=e.HasTeleconference
             }
             }
         });
         });
     }
     }
@@ -73,7 +87,8 @@ function handleConfirm(){
 <template>
 <template>
     <div class="report-list-classify-wrap">
     <div class="report-list-classify-wrap">
         <div class="top-box">
         <div class="top-box">
-            <span style="color:#666666" @click="handleReset">重置</span>
+            <span style="color:#666666" @click="handleCancle" v-if="props.noReset">取消</span>
+            <span style="color:#666666" @click="handleReset" v-else>重置</span>
             <span style="font-size:18px;font-weight:bold">选择分类</span>
             <span style="font-size:18px;font-weight:bold">选择分类</span>
             <span style="color:#0052D9" @click="handleConfirm">确定</span>
             <span style="color:#0052D9" @click="handleConfirm">确定</span>
         </div>
         </div>

+ 166 - 13
src/views/report/components/reportInsert/ETAChart.vue

@@ -1,34 +1,187 @@
 <script setup>
 <script setup>
-import {ref} from 'vue'
+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 { showToast } from 'vant'
+import { vInfiniteScroll } from '@vueuse/components'
+
+const emits=defineEmits(['update'])
 
 
 const searchVal=ref('')
 const searchVal=ref('')
-const typeActive=ref(0)
+const typeActive=ref('ETA图库')
 const onlyMe=ref(false)
 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=null
+    if(typeActive.value==='ETA图库'){
+        res=await apiChart.ETAChartListByES(params)
+    }else if(typeActive.value==='商品价格曲线'){
+        res=await apiFutureChart.searchChartList(params)
+    }else if(typeActive.value==='相关性图表'){
+        res=await apiCorrelationChart.searchChartList(params)
+    }else if(typeActive.value==='拟合方程曲线'){
+        res=await apiLineEquationChart.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
+    }
+    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>
 </script>
 
 
 <template>
 <template>
     <div class="ETA-chart-wrap">
     <div class="ETA-chart-wrap">
         <div class="sticky-box">
         <div class="sticky-box">
-            <van-search v-model="searchVal" shape="round" placeholder="请输入图表名称" />
-            <van-tabs v-model:active="typeActive" line-width="16px" title-active-color="#0052D9">
-                <van-tab title="ETA图库"></van-tab>
-                <van-tab title="商品价格曲线"></van-tab>
-                <van-tab title="相关性图表"></van-tab>
-                <van-tab title="拟合方程曲线"></van-tab>
+            <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="ETA图库" name="ETA图库"></van-tab>
+                <van-tab title="商品价格曲线" name="商品价格曲线"></van-tab>
+                <van-tab title="相关性图表" name="相关性图表"></van-tab>
+                <van-tab title="拟合方程曲线" name="拟合方程曲线"></van-tab>
             </van-tabs>
             </van-tabs>
-            <van-checkbox v-model="onlyMe">只看我的</van-checkbox>
+            <van-checkbox v-model="onlyMe" @change="handleRefreshList">只看我的</van-checkbox>
         </div>
         </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.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>
+            </ul>
+        </div>
     </div>
     </div>
 </template>
 </template>
 
 
 <style lang="scss" scoped>
 <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);
+    }
+    .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{
 .sticky-box{
-    position: sticky;
-    top: 0;
-    z-index: 10;
+    flex-shrink: 0;
+    // position: sticky;
+    // top: 0;
+    // z-index: 10;
     background-color: #fff;
     background-color: #fff;
     .van-tabs{
     .van-tabs{
         border-bottom: 1px solid $border-color;
         border-bottom: 1px solid $border-color;

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

@@ -1,10 +1,32 @@
 <script setup>
 <script setup>
 import {ref} from 'vue'
 import {ref} from 'vue'
 import ETAChart from './ETAChart.vue'
 import ETAChart from './ETAChart.vue'
+import MyETAChart from './MyETAChart.vue'
+import SandTableImg from './SandTableImg.vue'
+import SheetTableChart from './SheetTableChart.vue'
+
+const emits=defineEmits(['insert'])
 
 
 const typeOpt=['图表插入','批量插入','表格插入','沙盘插入']
 const typeOpt=['图表插入','批量插入','表格插入','沙盘插入']
 const activeType=ref('图表插入')
 const activeType=ref('图表插入')
 
 
+let list=ref([])
+
+function handleSelectChart(data){
+    list.value=data
+}
+
+function handleConfirmInsert(){
+    if(['图表插入','批量插入'].includes(activeType.value)){
+        emits('insert',{list:list.value,type:'iframe',chartType:'chart'})
+    }else if(activeType.value==='表格插入'){
+        emits('insert',{list:list.value,type:'iframe',chartType:'sheet'})
+    }else if(activeType.value==='沙盘插入'){
+        emits('insert',{list:list.value,type:'img',chartType:''})
+    }
+    
+    list.value=[]
+}
 </script>
 </script>
 
 
 <template>
 <template>
@@ -14,14 +36,17 @@ const activeType=ref('图表插入')
                 :class="['item',activeType===item&&'active']"
                 :class="['item',activeType===item&&'active']"
                 v-for="item in typeOpt" 
                 v-for="item in typeOpt" 
                 :key="item"
                 :key="item"
-                @click="activeType=item"
+                @click="activeType=item;list=[]"
             >{{item}}</li>
             >{{item}}</li>
         </ul>
         </ul>
         <div class="main-box">
         <div class="main-box">
-            <ETAChart />
+            <ETAChart @update="handleSelectChart" v-if="activeType==='图表插入'"/>
+            <MyETAChart @update="handleSelectChart" v-if="activeType==='批量插入'"/>
+            <SheetTableChart @update="handleSelectChart" v-if="activeType==='表格插入'"/>
+            <SandTableImg @update="handleSelectChart" v-if="activeType==='沙盘插入'"/>
         </div>
         </div>
         <div class="bot-btn">
         <div class="bot-btn">
-            <van-button type="primary" block>插入</van-button>
+            <van-button type="primary" block @click="handleConfirmInsert" :disabled="list.length===0">插入</van-button>
         </div>
         </div>
     </div>
     </div>
 </template>
 </template>

+ 195 - 0
src/views/report/components/reportInsert/MyETAChart.vue

@@ -0,0 +1,195 @@
+<script setup>
+import {reactive,ref,watch} from 'vue'
+import apiMyETAChart from '@/api/myETA'
+import { vInfiniteScroll } from '@vueuse/components'
+
+const emits=defineEmits(['update'])
+
+// 获取我的图库中分类
+const showClassify=ref(false)
+let classifyList=[]
+const activeClassify=ref('')
+const activeClassifyName=ref('')
+let curClassifyList=ref([])
+async function getClassifyList(){
+    const res=await apiMyETAChart.myClassifyList()
+    if(res.Ret===200){
+        classifyList=res.Data?.List??[]
+        curClassifyList.value=classifyList
+    }
+}
+getClassifyList()
+
+const searchVal=ref('')
+function handleRefreshList(){
+    activeClassify=ref('')
+    curClassifyList.value=classifyList.filter(item=>item.MyChartClassifyName.includes(searchVal.value))
+}
+
+function handleSave(){
+    activeClassifyName.value=classifyList.filter(item=>item.MyChartClassifyId==activeClassify.value)[0]?.MyChartClassifyName
+    showClassify.value=false
+    getChartList()
+}
+
+const listState=reactive({
+    page:1,
+    pageSize:10000,
+    list:[],
+    finished:false,
+    loading:false
+})
+async function getChartList(){
+    const params={
+        CurrentIndex:listState.page,
+        PageSize:listState.pageSize,
+        MyChartClassifyId:activeClassify.value
+    }
+    listState.loading=true
+    let res=await apiMyETAChart.myChartList(params)
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data.List||[]
+        listState.list=[...listState.list,...arr]
+        listState.finished=res.Data?.Paging.IsEnd
+    }
+}
+// 触底加载更多
+function onLoadMore(){
+    if(listState.finished||listState.loading) return
+    listState.page++
+    // getChartList()
+}
+
+watch(
+    ()=>listState.list,
+    ()=>{
+        emits('update',listState.list.map(item=>item.UniqueCode))
+    },
+    {
+        immediate:true
+    }
+)
+
+</script>
+
+<template>
+    <div class="myETA-insert-wrap">
+        <van-cell title="分类" :value="activeClassifyName" is-link @click="showClassify=true"/>
+        <div class="content-box">
+            <div v-if="listState.list.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">{{listState.finished?'暂无图表':'暂无图表,请先选择分类'}}</p>
+            </div>
+            
+            <ul class="chart-list" v-infinite-scroll="[onLoadMore, { 'distance' : 10 }]">
+                <li 
+                    class="chart-item active" 
+                    v-for="item in listState.list" 
+                    :key="item.ChartInfoId"
+                >
+                    <div class="van-multi-ellipsis--l2 title">{{item.ChartName}}</div>
+                    <img :src="item.ChartImage" alt="">
+                    <svg 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>
+            </ul>
+        </div>
+    </div>
+    <!-- 选择分类 -->
+    <van-popup
+        v-model:show="showClassify"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <div class="select-classify-wrap">
+            <van-search v-model="searchVal" shape="round" placeholder="请输入分类名称" @search="handleRefreshList" @clear="handleRefreshList" />
+            <ul class="list">
+                <van-radio-group v-model="activeClassify">
+                    <van-radio :name="item.MyChartClassifyId" v-for="item in curClassifyList" :key="item.MyChartClassifyId">{{item.MyChartClassifyName}}</van-radio>
+                </van-radio-group>
+            </ul>
+            <div class="btns">
+                <van-button @click="showClassify=false">取消</van-button>
+                <van-button type="primary" :disabled="activeClassify==''" @click="handleSave">保存</van-button>
+            </div>
+        </div>
+    </van-popup>
+</template>
+
+<style lang="scss" scoped>
+.myETA-insert-wrap{
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    .content-box{
+        overflow: hidden;
+        flex: 1;
+        padding: var(--van-padding-sm);
+    }
+    .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;
+            }
+        }
+    }
+}
+
+.select-classify-wrap{
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    .list{
+        overflow-y: auto;
+        flex: 1;
+        padding: var(--van-padding-sm) 0 var(--van-padding-sm) var(--van-padding-sm);
+        :deep(.van-radio__label){
+            flex: 1;
+            padding: 32px 0;
+            border-bottom: 1px solid $border-color;
+        }
+    }
+    .btns{
+        text-align: center;
+        padding: var(--van-padding-sm);
+        display: flex;
+        justify-content: space-around;
+        .van-button{
+            width: 315px;
+        }
+    }
+}
+</style>

+ 148 - 0
src/views/report/components/reportInsert/SandTableImg.vue

@@ -0,0 +1,148 @@
+<script setup>
+import {reactive, ref,watch} from 'vue'
+import apiSandBox from '@/api/sandBox'
+import { vInfiniteScroll } from '@vueuse/components'
+
+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 apiSandBox.getSandBoxList(params)
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data.List||[]
+        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.SandboxId)
+    if(index!==-1){
+        selectChartList.value.splice(index,1)
+    }else{
+        selectChartList.value.push(item.SandboxId)
+    }
+    
+}
+
+watch(
+    ()=>selectChartList.value,
+    ()=>{
+        const arr=listState.list.filter(item=>selectChartList.value.includes(item.SandboxId)).map(e=>e.PicUrl)
+        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.SandboxId)&&'active']" 
+                    v-for="item in listState.list" 
+                    :key="item.SandboxId"
+                    @click="handleSelect(item)"
+                >
+                    <div class="van-multi-ellipsis--l2 title">{{item.Name}}</div>
+                    <img :src="item.PicUrl" alt="">
+
+                    <svg v-if="selectChartList.includes(item.SandboxId)" 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>
+            </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);
+    }
+    .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;
+            }
+        }
+    }
+}
+</style>

+ 145 - 0
src/views/report/components/reportInsert/SheetTableChart.vue

@@ -0,0 +1,145 @@
+<script setup>
+import {reactive, ref,watch} from 'vue'
+import apiSheetChart from '@/api/sheet'
+import { vInfiniteScroll } from '@vueuse/components'
+
+const emits=defineEmits(['update'])
+
+const searchVal=ref('')
+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
+    }
+    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([])
+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="sheet-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.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.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>
+            </ul>
+        </div>
+    </div>
+</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);
+    }
+    .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;
+            }
+        }
+    }
+}
+</style>