浏览代码

Merge branch 'master' into eta1.1.6

Karsa 1 年之前
父节点
当前提交
03a35c33b8
共有 78 个文件被更改,包括 5721 次插入474 次删除
  1. 10 0
      src/api/modules/sheetApi.js
  2. 85 0
      src/api/modules/smartReport.js
  3. 二进制
      src/assets/img/icons/back_arrow_blue_new.png
  4. 二进制
      src/assets/img/icons/choose_bluebg_new.png
  5. 二进制
      src/assets/img/icons/download_blue.png
  6. 二进制
      src/assets/img/icons/edit_blue_new.png
  7. 二进制
      src/assets/img/icons/refresh_blue_new.png
  8. 二进制
      src/assets/img/icons/save_as_blue_new.png
  9. 二进制
      src/assets/img/icons/save_blue_new.png
  10. 二进制
      src/assets/img/ppt_m/add_first.png
  11. 二进制
      src/assets/img/smartReport/bg01.png
  12. 二进制
      src/assets/img/smartReport/icon01.png
  13. 二进制
      src/assets/img/smartReport/icon02.png
  14. 二进制
      src/assets/img/smartReport/icon03.png
  15. 二进制
      src/assets/img/smartReport/icon04.png
  16. 二进制
      src/assets/img/smartReport/icon05.png
  17. 二进制
      src/assets/img/smartReport/icon06.png
  18. 二进制
      src/assets/img/smartReport/icon07.png
  19. 二进制
      src/assets/img/smartReport/icon08.png
  20. 二进制
      src/assets/img/smartReport/icon09.png
  21. 二进制
      src/assets/img/smartReport/icon10.png
  22. 二进制
      src/assets/img/smartReport/icon11.png
  23. 二进制
      src/assets/img/smartReport/icon12.png
  24. 二进制
      src/assets/img/smartReport/icon13.png
  25. 二进制
      src/assets/img/smartReport/icon14.png
  26. 二进制
      src/assets/img/smartReport/icon15.png
  27. 二进制
      src/assets/img/smartReport/icon16.png
  28. 24 3
      src/routes/modules/chartRoutes.js
  29. 17 0
      src/routes/modules/oldRoutes.js
  30. 25 1
      src/utils/buttonConfig.js
  31. 5 92
      src/views/dataEntry_manage/chartSetting.vue
  32. 32 7
      src/views/dataEntry_manage/databaseList.vue
  33. 4 0
      src/views/dataEntry_manage/mixins/addOreditMixin.js
  34. 157 20
      src/views/datasheet_manage/addSheet.vue
  35. 40 4
      src/views/datasheet_manage/common/option.js
  36. 56 3
      src/views/datasheet_manage/components/CustomTable.vue
  37. 26 1
      src/views/datasheet_manage/components/MixedTable.vue
  38. 29 3
      src/views/datasheet_manage/components/SheetExcel.vue
  39. 1 1
      src/views/datasheet_manage/components/sheetListWrap.vue
  40. 1 1
      src/views/datasheet_manage/customAnalysis/addAnalysisSheet.vue
  41. 292 0
      src/views/datasheet_manage/customAnalysis/edit.vue
  42. 147 94
      src/views/datasheet_manage/customAnalysis/list.vue
  43. 196 78
      src/views/datasheet_manage/customSheetEdit.vue
  44. 88 14
      src/views/datasheet_manage/mixedSheetEdit.vue
  45. 4 24
      src/views/datasheet_manage/mixins/classifyMixin.js
  46. 121 94
      src/views/datasheet_manage/sheetList.vue
  47. 4 0
      src/views/mychart_manage/components/chartDetailDia.vue
  48. 2 2
      src/views/ppt_manage/newVersion/components/Cover.vue
  49. 2 2
      src/views/ppt_manage/newVersion/components/CoverEn.vue
  50. 1 1
      src/views/ppt_manage/newVersion/components/catalog/pptContent.vue
  51. 2 2
      src/views/ppt_manage/newVersion/pptEditor.vue
  52. 2 2
      src/views/ppt_manage/newVersion/pptEnEditor.vue
  53. 6 4
      src/views/ppt_manage/newVersion/pptEnPresent.vue
  54. 4 4
      src/views/ppt_manage/newVersion/pptEnPublish.vue
  55. 6 4
      src/views/ppt_manage/newVersion/pptPresent.vue
  56. 4 4
      src/views/ppt_manage/newVersion/pptPublish.vue
  57. 17 2
      src/views/predictEdb_manage/predictEdb.vue
  58. 2 1
      src/views/sandbox_manage/index.vue
  59. 312 0
      src/views/smartReport/components/BaseInfo.vue
  60. 20 0
      src/views/smartReport/components/ChartComp.vue
  61. 106 0
      src/views/smartReport/components/ETAChart.vue
  62. 177 0
      src/views/smartReport/components/ETAPriceChart.vue
  63. 169 0
      src/views/smartReport/components/ETASandBox.vue
  64. 130 0
      src/views/smartReport/components/ETASheet.vue
  65. 16 0
      src/views/smartReport/components/ImgComp.vue
  66. 141 0
      src/views/smartReport/components/ImgEdit.vue
  67. 142 0
      src/views/smartReport/components/ImportETAChart.vue
  68. 170 0
      src/views/smartReport/components/ImportMyETAChart.vue
  69. 169 0
      src/views/smartReport/components/SemanticAnalysis.vue
  70. 41 0
      src/views/smartReport/components/SheetComp.vue
  71. 243 0
      src/views/smartReport/components/StatisticAnalysis.vue
  72. 20 0
      src/views/smartReport/components/TextComp.vue
  73. 112 0
      src/views/smartReport/components/TextEdit.vue
  74. 1274 0
      src/views/smartReport/editReport.vue
  75. 239 0
      src/views/smartReport/reportDetail.vue
  76. 794 0
      src/views/smartReport/reportList.vue
  77. 33 6
      src/views/system_manage/newAuthManage.vue
  78. 1 0
      src/vuex/modules/permissionButton.js

+ 10 - 0
src/api/modules/sheetApi.js

@@ -98,6 +98,16 @@ export const sheetEdit = params => {
 	return http.post('/datamanage/excel_info/edit',params)
 }
 
+/**
+ * 标记表格的编辑状态 
+ * @param {*} params ExcelInfoId 
+ * @param {*} params Status  1:编辑中,2:编辑完成
+ * @returns 
+ */
+export const markSheetEditStatus = params => {
+	return http.post('/datamanage/excel_info/mark',params)
+}
+
 /**
  * 获取表格详情
  * @param {*} params ExcelInfoId 

+ 85 - 0
src/api/modules/smartReport.js

@@ -0,0 +1,85 @@
+import http from "@/api/http.js"
+
+//智能研报模块
+
+const apiSmartReport={
+    //报告列表
+    reportList:params=>{
+        return http.get('/smart_report/list',params)
+    },
+
+    //分类数据
+    classifyList:params=>{
+        return http.get('/classify/list',params)
+    },
+
+    //删除报告
+    delReport:params=>{
+        return http.post('/smart_report/remove',params)
+    },
+
+    //报告推送
+    reportMsgSend:params=>{
+        return http.post('/smart_report/send_msg',params)
+    },
+
+    // 作者
+    reportAuthor:params=>{
+        return http.get('/report/author',params)
+    },
+
+    //新增报告
+    reportAdd:params=>{
+        return http.post('/smart_report/add',params)
+    },
+
+    //编辑报告
+    reportEdit:params=>{
+        return http.post('/smart_report/edit',params)
+    },
+
+    //报告详情
+    reportDetail:params=>{
+        return http.get('/smart_report/detail',params)
+    },
+
+    //保存报告内容
+    saveReportContent:params=>{
+        return http.post('/smart_report/save_content',params)
+    },
+
+    //定时发布报告
+    prePublishReport:params=>{
+        return http.post('/smart_report/pre_publish',params)
+    },
+
+    //发布/取消发布报告
+    publishReport:params=>{
+        return http.post('/smart_report/publish',params)
+    },
+
+    //编辑状态
+    markReport:params=>{
+        return http.post('/smart_report/mark_edit',params)
+    },
+
+    //音频上传
+    voiceupload:params=>{
+        return http.post('/smart_report/voice_upload',params)
+    },
+
+    //报告导出图片
+    getReportImg:params=>{
+        return http.get('/smart_report/detail_img',params)
+    },
+
+    //获取上期已发布报告
+    getLastReport:params=>{
+        return http.get('/smart_report/last_published_report',params)
+    }
+
+}
+
+export {
+	apiSmartReport
+}

二进制
src/assets/img/icons/back_arrow_blue_new.png


二进制
src/assets/img/icons/choose_bluebg_new.png


二进制
src/assets/img/icons/download_blue.png


二进制
src/assets/img/icons/edit_blue_new.png


二进制
src/assets/img/icons/refresh_blue_new.png


二进制
src/assets/img/icons/save_as_blue_new.png


二进制
src/assets/img/icons/save_blue_new.png


二进制
src/assets/img/ppt_m/add_first.png


二进制
src/assets/img/smartReport/bg01.png


二进制
src/assets/img/smartReport/icon01.png


二进制
src/assets/img/smartReport/icon02.png


二进制
src/assets/img/smartReport/icon03.png


二进制
src/assets/img/smartReport/icon04.png


二进制
src/assets/img/smartReport/icon05.png


二进制
src/assets/img/smartReport/icon06.png


二进制
src/assets/img/smartReport/icon07.png


二进制
src/assets/img/smartReport/icon08.png


二进制
src/assets/img/smartReport/icon09.png


二进制
src/assets/img/smartReport/icon10.png


二进制
src/assets/img/smartReport/icon11.png


二进制
src/assets/img/smartReport/icon12.png


二进制
src/assets/img/smartReport/icon13.png


二进制
src/assets/img/smartReport/icon14.png


二进制
src/assets/img/smartReport/icon15.png


二进制
src/assets/img/smartReport/icon16.png


+ 24 - 3
src/routes/modules/chartRoutes.js

@@ -118,17 +118,38 @@ export default [
 			{
 				path:"addSheet",
 				name:"添加表格",
-				component:()=>import('@/views/datasheet_manage/addSheet.vue')
+				component:()=>import('@/views/datasheet_manage/addSheet.vue'),
+				meta: { 
+					pathFrom: "sheetList",
+					pathName: "在线Excel",
+				}
 			},
 			{
 				path:"addCustomSheet",
 				name:"添加数据表格",
-				component:()=>import('@/views/datasheet_manage/customSheetEdit.vue')
+				component:()=>import('@/views/datasheet_manage/customSheetEdit.vue'),
+				meta: { 
+					pathFrom: "sheetTimeList",
+					pathName: "时间序列表格",
+				}
 			},
 			{
 				path:"addMixedSheet",
 				name:"添加混合表格",
-				component:()=>import('@/views/datasheet_manage/mixedSheetEdit.vue')
+				component:()=>import('@/views/datasheet_manage/mixedSheetEdit.vue'),
+				meta: { 
+					pathFrom: "sheetMixedList",
+					pathName: "混合表格",
+				}
+			},
+			{
+				path:"editSheetAnalysis",
+				name:"编辑表格",
+				component:()=>import('@/views/datasheet_manage/customAnalysis/edit.vue'),
+				meta: { 
+					pathFrom: "sheetAnalysisList",
+					pathName: "自定义分析",
+				}
 			},
 			{
 				path:"sheetAnalysisList",

+ 17 - 0
src/routes/modules/oldRoutes.js

@@ -52,6 +52,18 @@ export default [
   	name:'AI问答',
   	hidden:false
   },
+  // 智能研报编辑页
+  {
+    path: "/smartReportEdit",
+    name: "智能报告",
+    component: () => import("@/views/smartReport/editReport.vue"),
+  },
+  // 智能研报详情页
+  {
+    path: "/smartReportDetail",
+    name: "智能报告",
+    component: () => import("@/views/smartReport/reportDetail.vue"),
+  },
 
   // 主页
   {
@@ -235,6 +247,11 @@ export default [
         name: "英文品种配置",
         component: () => import("@/views/report_manage/reportVariety.vue"),
       },
+      {
+        path: "smartReportList",
+        name: "智能研报",
+        component: () => import("@/views/smartReport/reportList.vue"),
+      },
     ],
   },
 

+ 25 - 1
src/utils/buttonConfig.js

@@ -34,6 +34,26 @@ export const reportManageBtn={
     reportManage_dayWeekReportAdd:'reportManage:dayWeekReportAdd',//添加晨报周报
     reportManage_reportAdd:'reportManage:reportAdd',//添加研报
 }
+/*
+ *--------智能研报列表----------- 
+ */
+ export const smartReportManageBtn={
+    reportManage_sendMsg:'smartReportManage:sendMsg',//推送消息/已推送消息
+    reportManage_reportView:'smartReportManage:reportView',//研报预览:即是否能点击研报名称跳转预览页面
+    reportManage_reportView_wechartShare:'smartReportManage:reportView:wechartShare',//研报预览页面-微信分享
+    reportManage_reportView_copyWechat:'smartReportManage:reportView:copyWechat',//研报预览页面-复制链接
+    reportManage_reportView_exportImg:'smartReportManage:reportView:exportImg',//研报预览页面-导出图片
+    reportManage_audioDownload:'smartReportManage:audioDownload',//音频下载
+    reportManage_audioUpload:'smartReportManage:audioUpload',//音频上传
+    reportManage_reportDel:'smartReportManage:reportDel',//删除研报
+    reportManage_reportEdit:'smartReportManage:reportEdit',//编辑研报
+    reportManage_cancelPublish:'smartReportManage:cancelPublish',//取消发布
+    reportManage_publish:'smartReportManage:publish',//发布研报
+    reportManage_reportList:'smartReportManage:reportList',//研报列表的选项
+    reportManage_reportList_uv:'smartReportManage:reportList:uv',//研报列表-PV/UV
+    reportManage_reportList_sendTime:'smartReportManage:reportList:sendTime',//研报列表-报告推送时间
+    reportManage_reportAdd:'smartReportManage:reportAdd',//添加研报
+}
 /*
 *--------英文研报列表----------- 
 */
@@ -365,6 +385,7 @@ export const etaTablePermission = {
     etaTable_customize_mix_refresh:'etaTable:customize:mix:refresh',//刷新
     etaTable_customize_mix_otherSave:'etaTable:customize:mix:otherSave',//另存为
     etaTable_customize_mix_download:'etaTable:customize:mix:download',//下载
+    etaTable_customize_mix_save:'etaTable:customize:mix:save',//保存
     etaTable_customize_mix_del:'etaTable:customize:mix:del',//删除
 
     //数据表格页面
@@ -376,6 +397,7 @@ export const etaTablePermission = {
     etaTable_customize_data_otherSave:'etaTable:customize:data:otherSave',//另存为
     etaTable_customize_data_download:'etaTable:customize:data:download',//下载
     etaTable_customize_data_del:'etaTable:customize:data:del',//删除
+    etaTable_customize_data_save:'etaTable:customize:data:save',//保存
 
     /*-----------excel表格页面--------- */
     etaTable_excel:'etaTable:excel',//添加Excel表格这个按钮显示不显示
@@ -383,7 +405,8 @@ export const etaTablePermission = {
     etaTable_excel_classifyOpt_delete:'etaTable:excel:classifyOpt:delete',//删除表格
     etaTable_excel_del:'etaTable:excel:del',
     etaTable_excel_download:'etaTable:excel:download',
-    etaTable_excel_save:'etaTable:excel:save',
+    etaTable_excel_save:'etaTable:excel:save',//保存
+    etaTable_excel_edit:'etaTable:excel:edit',
 
     //自定义分析表格页面
     // etaTable_analysis_sheetAdd: 'etaTable:analysis:sheetAdd',//添加数据表格按钮
@@ -396,6 +419,7 @@ export const etaTablePermission = {
     etaTable_analysis_download:'etaTable:analysis:download',//下载
     etaTable_analysis_del:'etaTable:analysis:del',//删除
     etaTable_analysis_save:'etaTable:analysis:save',//保存
+    etaTable_analysis_edit:'etaTable:analysis:edit',//编辑
 }
 /*
  * --------------------------------------------------------------------------ETA逻辑------------------------------------------------

+ 5 - 92
src/views/dataEntry_manage/chartSetting.vue

@@ -1671,90 +1671,12 @@ export default {
       this.tableData.forEach((item) => {
         let edbData = EdbInfoList.find(_ => _.EdbInfoId===item.EdbInfoId);
         item.DataList = edbData.DataList;
+        //更新起始时间和最近更新时间
+        item.StartDate = edbData.StartDate;
+        item.ModifyTime = edbData.ModifyTime;
+
         if(edbData.EdbInfoCategoryType===1) item.MoveLatestDate = edbData.MoveLatestDate;
       });
-
-
-
-      // // 判断图标类型 设置参数
-      // let params =
-      //   this.sameOptionType.includes(this.selected_chartType)
-      //     ? {
-      //         ChartInfoId: this.selected_chartid,
-      //         DateType: this.year_select,
-      //         StartDate:
-      //           this.year_select === 5 || this.year_select === 6
-      //             ? this.select_date[0]
-      //             : '',
-      //         EndDate: this.year_select === 5 ? this.select_date[1] : '',
-      //       }
-      //     : {
-      //         ChartInfoId: this.selected_chartid,
-      //         Calendar: this.calendar_type,
-      //         SeasonStartDate: this.season_year ? this.season_year[0] : '',
-      //         SeasonEndDate: this.season_year ? this.season_year[1] : '',
-      //       };
-      // dataBaseInterface.chartInfo(params).then((res) => {
-      //   if (res.Ret === 200) {
-      //     this.chartInfo = res.Data.ChartInfo;
-      //     let beforeOptions = sessionStorage.getItem('beforeOptions')
-      //       ? JSON.parse(sessionStorage.getItem('beforeOptions'))
-      //       : '';
-      //     //合并缓存配置和新的数据
-      //     let newarr = res.Data.EdbInfoList.map((item, index) => {
-      //       if (beforeOptions && type === 'refresh') {
-      //         const DataList = item.DataList;
-      //         return {
-      //           ...beforeOptions[index],
-      //           DataList,
-      //         };
-      //       } else {
-      //         return item;
-      //       }
-      //     });
-      //     this.tableData = newarr;
-      //     //  刷新最新的指标数据 防止领先配置已保存 指标数据确实原数据的情况 针对正常图
-      //     if (
-      //       type === 'refresh' &&
-      //       beforeOptions &&
-      //       (this.sameOptionType.includes(this.chartInfo.ChartType) && this.chartInfo.ChartType!==5)
-      //     )
-      //       this.refreshTarget();
-      //     sessionStorage.setItem(
-      //       'defaultArr',
-      //       JSON.stringify(res.Data.EdbInfoList)
-      //     );
-      //     if (!type) {
-      //       this.year_select = this.chartInfo.DateType;
-      //       this.select_date = [
-      //         this.chartInfo.StartDate,
-      //         this.chartInfo.EndDate,
-      //       ];
-      //       this.calendar_type = this.chartInfo.Calendar; //日历类型
-      //       // this.season_year = [ this.chartInfo.SeasonStartDate, this.chartInfo.SeasonEndDate ];
-      //       this.dateTip =
-      //         this.chartInfo.DateType === 5
-      //           ? `${this.chartInfo.StartDate}~${this.chartInfo.EndDate}`
-      //           : this.chartInfo.DateType === 6
-      //           ? `${this.chartInfo.StartDate}~至今`
-      //           : '请选择时间段';
-
-      //       //新图表类型 依赖数据不同单独init 数据
-      //       const typeInitMap = {
-      //         7: this.initBarData,
-      //         10: this.initSectionScatterData
-      //       }
-      //       typeInitMap[this.chartInfo.ChartType] && typeInitMap[this.chartInfo.ChartType](res.Data);
-            
-      //     }
-      //     //将指标添加进标签列表中
-      //       const {ChartNameEn,ChartName,ChartInfoId,UniqueCode,ChartClassifyId}=res.Data.ChartInfo
-      //       this.addLabel({code:UniqueCode,id:ChartInfoId,classifyId:ChartClassifyId,EdbName:ChartName,EdbNameEn:ChartNameEn,chartData:res.Data.ChartInfo})
-      //       this.defaultShowNodes=this.findParentNodeHandle(this.treeData,ChartClassifyId)
-      //       this.changeTreeNode()
-      //   }
-        
-      // });
     },
 
     /* 搜索 */
@@ -2243,16 +2165,7 @@ export default {
     slideHandle() {
       this.isSlideLeft = !this.isSlideLeft;
     },
-    /* 保存图表当前配置项 上下限 */
-    saveNowOptions() {
-      // const dataArr = _.cloneDeep(this.tableData);
-      // dataArr.forEach((item) => {
-      //   item.MaxData = Number(item.MaxData);
-      //   item.MinData = Number(item.MinData);
-      //   delete item.DataList;
-      // });
-      // sessionStorage.setItem('beforeOptions', JSON.stringify(dataArr));
-    },
+
     /* 季节图切换年份  保持当前配置 */
     seasonYearChange() {
       // this.saveNowOptions();

+ 32 - 7
src/views/dataEntry_manage/databaseList.vue

@@ -1209,9 +1209,23 @@ export default {
 					EdbInfoId: data.EdbInfoId
 				}).then(res => {
 					if(res.Ret === 200) {
-						this.setComputedDialogForm(data.Source,node,data,res.Data);
-						this.computed_type = [23,24].includes(data.Source) ? 'joint' : data.Source;
-						this.computed_type = [72,73].includes(data.Source)? 'alpha': data.Source;
+						this.setComputedDialogForm(data.Source,res.Data);
+						
+						switch (data.Source) {
+							case 23: 
+							case 24: 
+								this.computed_type = 'joint';
+								break
+							case 72:
+							case 73:
+								this.computed_type = 'alpha';
+								break
+							default:
+								this.computed_type = data.Source;
+								break
+						}
+						// this.computed_type = [23,24].includes(data.Source) ? 'joint' : data.Source;
+						// this.computed_type = [72,73].includes(data.Source)? 'alpha': data.Source;
 					}
 				})
 
@@ -1609,14 +1623,25 @@ export default {
 				EdbInfoId,
 			}).then(res => {
 				if(res.Ret !== 200) return
-				this.setComputedDialogForm(Source,node,data,res.Data,true);
-				this.computed_type = [23,24].includes(Source) ? 'joint' : Source;
-				this.computed_type = [72,73].includes(data.Source)? 'alpha': data.Source;
+				this.setComputedDialogForm(Source,res.Data,true);
+				switch (data.Source) {
+					case 23: 
+					case 24: 
+						this.computed_type = 'joint';
+						break
+					case 72:
+					case 73:
+						this.computed_type = 'alpha';
+						break
+					default:
+						this.computed_type = data.Source;
+						break
+				}
 			})
 		},
 
 		/* 设置回显计算指标的表单 */
-		setComputedDialogForm(type,node,data,res,view=false) {
+		setComputedDialogForm(type,res,view=false) {
 			//指标运算 or 其他计算类型指标
 
 			// 处理所在目录

+ 4 - 0
src/views/dataEntry_manage/mixins/addOreditMixin.js

@@ -313,6 +313,10 @@ export default {
       this.tableData.forEach((item) => {
         let edbData = EdbInfoList.find(_ => _.EdbInfoId===item.EdbInfoId);
         item.DataList = edbData.DataList;
+				//更新起始时间和最近更新时间
+        item.StartDate = edbData.StartDate;
+        item.ModifyTime = edbData.ModifyTime;
+				
         if(edbData.EdbInfoCategoryType===1) item.MoveLatestDate = edbData.MoveLatestDate;
       });
 		},

+ 157 - 20
src/views/datasheet_manage/addSheet.vue

@@ -7,13 +7,14 @@
             v-model="sheetForm.name"
             placeholder="请输入表格名称"
             clearable
-            style="width:300px">
+            style="width:240px">
           </el-input>
         </li>
         <li>
           <el-select 
             v-model="sheetForm.classify" 
             placeholder="请选择表格分类"
+            style="width:240px"
             clearable
           >
 							<el-option
@@ -25,14 +26,21 @@
 						</el-select>
         </li>
       </ul>
+      <div v-if="updateTime" style="color:#999999;margin-right: 30px;">最近保存时间:{{ updateTime }}</div>
       <div>
-        <el-button type="primary" size="medium" @click="saveSheetHandle">保存</el-button>
+        <el-button type="primary" size="medium" @click="saveSheetHandle" v-if="hasPermission">保存</el-button>
         <el-button type="primary" size="medium" plain @click="backHandle">返回</el-button>
       </div>
     </div>
     
     <div class="main">
-      <Sheet ref="sheetRef"/>
+      <Sheet ref="sheetRef" :limit="{disabled:false}" :option="this.option" v-if="sheetInit" @updated="autoSaveFun"
+      :sheetInfo="{
+        ExcelInfoId: sheetForm.infoId,
+        ExcelName: sheetForm.name,
+        ExcelClassifyId: sheetForm.classify,
+        Source: sheetForm.source
+      }" />
     </div>
   </div>
 </template>
@@ -45,14 +53,56 @@ export default {
   components: { Sheet },
   data() {
     return {
+      sheetId: this.$route.query.id || '',
+      isCanEdit:false,
       sheetForm: {},
-      classifyArr: []
+      classifyArr: [],
+      sheetInit:false,
+      option:{},
+      updateTime:'',
+      sheetButton:'',
+      cancelAutoSave:false
+    }
+  },
+  beforeRouteEnter(to, from, next) {
+    if(to.query.id){
+      to.matched[1].name=`编辑表格`
+    }else{
+      to.matched[1].name='添加表格'
+    }
+    next()
+  },
+  beforeRouteLeave(to,from,next){
+    if(to.path!='/addsheet'){
+      this.markFinishStatus()
+    }
+    next()
+  },
+  watch:{
+    sheetForm:{
+      handler(newVal){
+        console.log(this.sheetInit,this.sheetId);
+        if(this.sheetId && this.sheetInit) this.autoSaveFun()
+      },
+      deep:true
+    }
+  },
+  computed:{
+    hasPermission(){
+      // console.log(this.sheetButton,'sheetButton');
+      return this.sheetButton?
+            this.permissionBtn.isShowBtn('etaTablePermission','etaTable_excel_save')&&this.sheetButton.OpButton:
+            this.permissionBtn.isShowBtn('etaTablePermission','etaTable_excel_save')
     }
   },
   methods: {
 
     backHandle() {
-      window.close();
+      // if(this.sheetId){
+      //   this.$router.back()
+      // }else{
+        window.close();
+      // }
     },
 
     /* 获取分类 */
@@ -64,12 +114,76 @@ export default {
       })
     },
 
+    /* 获取表格详情 */
+    async getDetail() {
+      if(!this.sheetId){
+        this.sheetInit=true
+        return
+      }
+
+      const res = await sheetInterface.sheetDetail({
+				ExcelInfoId: Number(this.sheetId)
+			})
+
+      if(res.Ret !== 200)  return
+
+      this.isCanEdit = res.Data.CanEdit
+      if(!res.Data.CanEdit){
+        this.$message.warning(`${res.Data.Editor}正在编辑中`)
+        setTimeout(()=>{
+          this.backHandle()
+        },1000)
+        return 
+      }
+      const { ExcelInfoId,ExcelName,ExcelClassifyId,ExcelType,ModifyTime,Content,Source} = res.Data;
+      this.sheetForm = {
+        infoId:ExcelInfoId,
+        name: ExcelName,
+        classify: ExcelClassifyId,
+        sheetType: ExcelType,
+        source:Source
+      }
+      this.updateTime =  this.$moment(ModifyTime).format('YYYY-MM-DD HH:mm:ss')
+
+      this.option={
+        data: [{
+          ...JSON.parse(Content),
+          scrollTop: 0,
+          scrollLeft: 0
+        }]
+      }
+
+      this.$nextTick(()=>{
+        this.sheetInit=true
+      })
+
+    },
+    autoSaveFun:_.debounce(async function(){
+      if(!this.sheetId && this.sheetInit || this.cancelAutoSave) return
+      const { name,classify,infoId } = this.sheetForm;
+      luckysheet.exitEditMode()
+      let data = luckysheet.getAllSheets()[0];
+      if(!name || !classify) return this.$message.warning(name ? '请选择表格分类' : '请输入表格名称')
+      if(!data.celldata.length) return this.$message.warning('请输入表格内容');
+      let params={
+        ExcelInfoId:infoId,
+        ExcelName: name,
+        ExcelClassifyId: classify,
+        ExcelImage: "",
+        Content: JSON.stringify(data)
+      }
+      const res = await sheetInterface.sheetEdit(params)
+      if(res.Ret !==200) return
+      this.updateTime = this.$moment().format('YYYY-MM-DD HH:mm:ss')
 
+      // console.log("自动保存");
+    },1500),
     /* 保存表格 */
     saveSheetHandle: _.debounce(async function() {
-      const { name,classify } = this.sheetForm;
+      const { name,classify} = this.sheetForm;
       luckysheet.exitEditMode()
-      let data = luckysheet.getAllSheets()[0]
+      //结构类型乱飘 强制定义下
+      let data = {...luckysheet.getAllSheets()[0],status:Number(luckysheet.getAllSheets()[0].status)}
       if(!name || !classify) return this.$message.warning(name ? '请选择表格分类' : '请输入表格名称')
       if(!data.celldata.length) return this.$message.warning('请输入表格内容');
 
@@ -86,31 +200,53 @@ export default {
 			const { Data } = await sheetInterface.uploadImg(form)
 
       data.luckysheet_select_save = [];
-      const res = await sheetInterface.sheetAdd({
+      let params={
         ExcelName: name,
         ExcelClassifyId: classify,
         ExcelImage: Data.ResourceUrl,
         Content: JSON.stringify(data)
-      })
-      this.loading.close()
-      if(res.Ret !== 200) return
+      }
+      let isAdd = this.sheetId?false:true
+      const res = this.sheetId
+      ? await sheetInterface.sheetEdit({ ExcelInfoId: Number(this.sheetId),...params })
+      : await sheetInterface.sheetAdd(params)
 
+      this.loading.close()
+      if(res.Ret !==200) return
+      this.updateTime = this.$moment().format('YYYY-MM-DD HH:mm:ss')
+      
+      this.sheetId = this.sheetId || res.Data.ExcelInfoId;
       this.$message.success(res.Msg);
+      if(isAdd){
+        this.$router.replace({path:'/addsheet',query:{id:this.sheetId}})
+        this.cancelAutoSave=true
+      }
 
-      const { ExcelInfoId, UniqueCode } = res.Data;
+
+      // const { ExcelInfoId, UniqueCode } = res.Data;
       
-      this.$router.replace({
-        path: '/sheetList',
-        query: {
-          code: UniqueCode,
-          id: ExcelInfoId
-        }
-      })
+      // this.$router.replace({
+      //   path: '/sheetList',
+      //   query: {
+      //     code: UniqueCode,
+      //     id: ExcelInfoId
+      //   }
+      // })
     },300),
-    
+    markFinishStatus(){
+      if((!this.sheetId) || (!this.isCanEdit)) return
+      sheetInterface.markSheetEditStatus({ExcelInfoId: +this.sheetId,Status:2}).then(res=>{
+        if(res.Ret != 200) return 
+      })
+    }
   },
   mounted() {
+    this.getDetail()
     this.getClassify();
+    window.addEventListener('beforeunload',this.markFinishStatus)
+  },
+  beforeDestroy(){
+    window.removeEventListener('beforeunload',this.markFinishStatus)
   }
 }
 </script>
@@ -118,6 +254,7 @@ export default {
 *{ box-sizing: border-box; }
 .addSheet-wrap {
   min-height: calc(100vh - 120px);
+  min-width: 1000px;
   .wrap-top {
     display: flex;
     justify-content: space-between;

+ 40 - 4
src/views/datasheet_manage/common/option.js

@@ -2,9 +2,13 @@
 /*  初始化  
   options 其他配置 包括初始化数据 data:[{ celldata:[] }]
   sheetInfo 表格id相关信息 用来内容hooks变化时保存草稿
+  limit 限制性数据
+  callbackItems 回调函数组成的对象
 */
 import * as sheetInterface from '@/api/modules/sheetApi.js';
-export function initSheet(container,options={},sheetInfo={}) {
+import { Message } from 'element-ui';
+
+export function initSheet(container,options={},sheetInfo={},limit,callbackItems) {
   const configOpt = {
     container,
     lang: 'zh', // 设定表格语言
@@ -17,13 +21,45 @@ export function initSheet(container,options={},sheetInfo={}) {
       postil:  false, //'批注'
 		},
     cellRightClickConfig: {
+      copy:!limit.disabled,
+      copyAs:!limit.disabled,
       chart: false, // 图表生成
       image: false, // 插入图片
       link: false, // 插入链接
     },
+    allowCopy:!limit.disabled,//没效果
     hook: {
+      cellEditBefore:(range)=>{
+        if(limit.disabled){
+          Message.warning("当前不可编辑")
+          setTimeout(()=>{
+            luckysheet.exitEditMode();
+          },0)
+        }
+      },
+      cellUpdateBefore:()=>{
+        if(limit.disabled){
+          // 不可编辑
+          return false
+        }
+      },
+      // rangeCopyBefore:(range,data)=>{
+      //   // 不触发
+      //   console.log(range,data,'range,data','rangeCopyBefore');
+      // },
+      // rangeCopyAfter:(range,data)=>{
+      //   // 不触发
+      //   console.log(range,data,'range,data','rangeCopyAfter');
+      // },
       updated: (a,b,c,d,e)=> {
-
+        if(limit.disabled){
+          Message.warning("当前不可编辑")
+          luckysheet.undo()
+          return 
+        }
+        // console.log(callbackItems);
+        callbackItems.updated()
+        //草稿
         if(sheetInfo.Source&&sheetInfo.Source===1) {
           let data = luckysheet.getAllSheets()[0];
           data.luckysheet_select_save = [];
@@ -35,9 +71,9 @@ export function initSheet(container,options={},sheetInfo={}) {
             Content: JSON.stringify(data)
           })
         }
-      }
+      },
     },
-    ...options
+    ...options,
   }
 
   luckysheet.create(configOpt)

+ 56 - 3
src/views/datasheet_manage/components/CustomTable.vue

@@ -428,6 +428,31 @@ export default {
       this.$nextTick(() => {
         resetStyle();
       })
+    },
+    // 'config.data':{
+    //   handler(value){
+    //     console.log(value,'valuevaluevalue');
+    //     if(!this.disabled && this.hasInit){
+    //       this.$emit("autoSave")
+    //     }
+    //   },
+    //   deep:true
+    // },
+    'config.textRowData':{
+      handler(value){
+        if(!this.disabled && this.hasInit){
+          this.$emit("autoSave")
+        }
+
+      },
+      deep:true
+    },
+    'config.order':{
+      handler(value){
+        if(!this.disabled && this.hasInit){
+          this.$emit("autoSave")
+        }
+      }
     }
   },
   components: {addDateCellDia,mDialog},
@@ -479,7 +504,8 @@ export default {
       isEditEdbAliasDialog: false,
       editEdb: {},
 
-      copyCellItem: {}
+      copyCellItem: {},
+      hasInit:false
     };
   },
   methods: {
@@ -505,6 +531,9 @@ export default {
             
         })
       }
+      if(!this.disabled && this.hasInit){
+        this.$emit("autoSave")
+      }
     },
 
     /* 改变排序 */
@@ -517,6 +546,9 @@ export default {
           ? edb.Data.sort((x,y) => new Date(y.DataTime)-new Date(x.DataTime))
           : edb.Data.sort((x,y) => new Date(x.DataTime)-new Date(y.DataTime))
       })
+      if(!this.disabled && this.hasInit){
+        this.$emit("autoSave")
+      }
     },
 
     // 单击指标名称修改别名
@@ -534,6 +566,9 @@ export default {
     /* 保存别名 */
     saveEdbAlias() {
       this.config.data.find(_ =>_.EdbInfoId === this.editEdb.EdbInfoId).EdbAliasName = this.editEdb.EdbAliasName;
+      if(!this.disabled && this.hasInit){
+        this.$emit("autoSave")
+      }
       this.isEditEdbAliasDialog = false;
     },
 
@@ -590,6 +625,9 @@ export default {
           ? [...edb.Data,...edb_concat_data].sort((x,y) => new Date(y.DataTime)-new Date(x.DataTime))
           : [...edb.Data,...edb_concat_data].sort((x,y) => new Date(x.DataTime)-new Date(y.DataTime))
       })
+      if(!this.disabled && this.hasInit){
+        this.$emit("autoSave")
+      }
     },
 
     /* 输入框失焦 存值或公式计算 val,行标 列标 指标信息 */
@@ -600,6 +638,9 @@ export default {
       if(!value){
         cell.DataType = 3;
         cell.ShowValue = '';
+        if(!this.disabled && this.hasInit){
+          this.$emit("autoSave")
+        }
         return
       }
 
@@ -613,7 +654,10 @@ export default {
         cell.ShowValue = value
       }
       this.$set(cell,'CanEdit',false)
-      
+
+      if(!this.disabled && this.hasInit){
+        this.$emit("autoSave")
+      }
     },
 
     /* 输入公式的计算值 */
@@ -706,7 +750,9 @@ export default {
 
         
       }
-
+      if(!this.disabled && this.hasInit){
+        this.$emit("autoSave")
+      }
       this.hideContextMenu()
     },
 
@@ -757,6 +803,7 @@ export default {
 
     /* 详情initData */
     initSheetData(initData) {
+      this.hasInit=false
       if(initData.Data) {
         const { Data,Sort,TextRowData } = initData;
 
@@ -764,6 +811,9 @@ export default {
         this.config.data = Data;
         this.config.textRowData = TextRowData;
       }
+      this.$nextTick(()=>{
+        this.hasInit=true
+      })
     },
 
     /* 跳转到指标库 */
@@ -786,6 +836,9 @@ export default {
     /* 清空所有数据 */
     reset() {
       this.config.data = [];
+      if(!this.disabled && this.hasInit){
+        this.$emit("autoSave")
+      }
       this.config.textRowData = [];
     },
 

+ 26 - 1
src/views/datasheet_manage/components/MixedTable.vue

@@ -227,6 +227,24 @@ export default {
       return getRowHeaderCode(total_length);
     },
   },
+  watch:{
+    'config.data':{
+      handler(newVal){
+        if(!this.disabled && this.hasInit){
+          this.$emit("autoSave")
+        }
+      },
+      deep:true
+    },
+    insertRelationArr:{
+      handler(newVal){
+        if(!this.disabled && this.hasInit){
+          this.$emit("autoSave")
+        }
+      },
+      deep:true
+    },
+  },
   data() {
     return {
       config: {
@@ -271,7 +289,9 @@ export default {
 
       },
 
-      formulaTip
+      formulaTip,
+      
+      hasInit:false,
     };
   },
   mounted() {
@@ -898,6 +918,8 @@ export default {
 
     /* 初始化8行5列 */
     initData(initData=null) {
+      console.log('initData');
+      this.hasInit=false
       if(initData) {
         const { CellRelation,Data } = initData;
         this.config.data = Data;
@@ -916,6 +938,9 @@ export default {
           }));
         });
       }
+      this.$nextTick(()=>{
+        this.hasInit=true
+      })
     },
 
 

+ 29 - 3
src/views/datasheet_manage/components/SheetExcel.vue

@@ -14,7 +14,13 @@ export default {
     sheetInfo: {
       type: Object,
       default: ()=>{}
-    }
+    },
+    limit: {
+      type: Boolean,
+      default: ()=>{
+        return {disabled:false}
+      }
+    },
   },
   data() {
     return {
@@ -24,8 +30,20 @@ export default {
   methods: {
     init() {
       let optionData = this.option ? this.option : {};
-
-      initSheet('sheet-container',optionData,this.sheetInfo)
+      let callbackItems={updated:this.updateEmit}
+      initSheet('sheet-container',optionData,this.sheetInfo,this.limit,callbackItems)
+    },
+    copyDisable(e){
+      // 变向的禁止复制
+      // console.log(e.target.value && e.target.value.indexOf('lucksheet'));
+      if(e.target.value && e.target.value.indexOf('lucksheet')){
+        luckysheet.enterEditMode();
+        // luckysheet.exitEditMode();
+        return false
+      }
+    },
+    updateEmit(){
+      this.$emit("updated")
     }
   },
   destroyed() {
@@ -33,7 +51,15 @@ export default {
   },
   mounted() {
     this.init();
+    if(this.limit.disabled){
+      document.addEventListener('copy',this.copyDisable)
+    }
   },
+  beforeDestroy(){
+    if(this.limit.disabled){
+      document.removeEventListener('copy',this.copyDisable)
+    }
+  }
 }
 </script>
 <style scoped lang="scss">

+ 1 - 1
src/views/datasheet_manage/components/sheetListWrap.vue

@@ -30,7 +30,7 @@
                 >下载</span
               >
               <span
-                v-if="$parent.isSheetBtnShow('del')"
+                v-if="($parent.sheetDetailInfo.Button && $parent.sheetDetailInfo.Button.DeleteButton && $parent.isSheetBtnShow('del'))"
                 class="deletesty"
                 @click="$emit('delSheetHandle',{cell, type:'del-list'})"
                 >删除</span

+ 1 - 1
src/views/datasheet_manage/customAnalysis/addAnalysisSheet.vue

@@ -22,7 +22,7 @@
           />
         </el-tabs>
         <div class="sheet-wrapper">
-          <Sheet ref="sheetRef" :option="sheetConfig" v-if="sheetConfig.data"/>
+          <Sheet ref="sheetRef" :option="sheetConfig" v-if="sheetConfig.data" :limit="{disabled:false}"/>
 
           <dataLoading :loading="isLoading"/>
         </div>

+ 292 - 0
src/views/datasheet_manage/customAnalysis/edit.vue

@@ -0,0 +1,292 @@
+<template>
+  <div class="sheet-detail-wrapper" >
+    <div class="detail-top">
+      <el-input
+        ref="sheetEditTitRef"
+        style="width: 400px"
+        placeholder="请输入表格名称"
+        class="label-input"
+        v-model="sheet_title"
+        v-if="sheetDetailInfo.isEditTit"
+        @blur="changeValue(sheetDetailInfo, 'edit-tit')"
+      />
+      <span
+        class="sheet-name"
+        @click="editNodeLabel(sheetDetailInfo, 'edit-tit')"
+        v-else
+      >
+        {{ sheetDetailInfo.ExcelName }}
+        <i class="el-icon-edit"/>
+      </span>
+      <div class="sheet-anothor-info">
+        <span class="author">作者:{{ sheetDetailInfo.SysUserRealName }}</span>
+        <ul class="action-ul" v-if="sheetDetailInfo.Button">
+          <div v-if="updateTime" style="color:#999999;">最近保存时间:{{ updateTime }}</div>
+          <el-tooltip effect="dark" content="在当前表格选择日期列和数据列生成指标" placement="top-start">
+              <li class="editsty" @click="HandleToPath" v-if="isSheetBtnShow('createedb')&&sheetDetailInfo.Button.OpEdbButton">
+                <img src="~@/assets/img/icons/choose_bluebg_new.png"/>
+                <span>生成指标</span>
+              </li>
+          </el-tooltip>
+
+          <el-tooltip effect="dark" content="根据表格保存的最新内容,更新当前表格生成的所有指标" placement="top-start">
+              <li class="editsty" @click="refreshSheet" v-if="isSheetBtnShow('refresh')&&sheetDetailInfo.Button.RefreshEdbButton">
+                <img src="~@/assets/img/icons/refresh_blue_new.png"/>
+                <span>刷新指标</span>
+              </li>
+          </el-tooltip>
+          <li class="editsty" @click="saveHandle" v-if="isSheetBtnShow('save')&&sheetDetailInfo.Button.OpButton">
+            <img src="~@/assets/img/icons/save_blue_new.png"/>
+            <span>保存</span>
+          </li>
+          <li class="editsty" @click="backHandle">
+            <img src="~@/assets/img/icons/back_arrow_blue_new.png"/>
+            <span>返回</span>
+          </li>
+        </ul>
+      </div>
+    </div>
+
+    <dataLoading :loading="isSheetLoading"/>
+
+    <!-- 表格 -->
+    <div class="sheet-wrap">
+      <Sheet
+        ref="sheetRef"
+        v-if="sheetConfigOpt.data"
+        :option="sheetConfigOpt"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import * as sheetInterface from "@/api/modules/sheetApi.js";
+import Sheet from "../components/SheetExcel.vue";
+import leftMixin from "../mixins/classifyMixin";
+import { getSheetImage } from "../common/option";
+
+  export default {
+    components:{Sheet},
+    mixins: [leftMixin],
+    data() {
+      return {
+        sheetDataPage: 2,
+        sheetAllcellData:[],//全部单元格数据 分页push
+        dataToalPage: 0,
+        isSheetLoading: false,
+        code:'',
+        sheetConfigOpt: {
+          showsheetbar: true,
+          data: null
+        },
+        sheet_title: "", //表格标题 双击标题修改时来存储最新值
+        sheetDetailInfo:{},
+        isCanEdit:false,
+        sheetId:'',
+        loading:null,
+        updateTime:''
+      }
+    },
+    beforeRouteLeave(to,from,next){
+      if(to.path!='/editSheetAnalysis'){
+        this.markFinishStatus()
+      }
+      next()
+    },
+    created(){
+      // console.log(this.$route.query.code);
+      if(!this.$route.query.code){
+        this.$message.warning("参数丢失")
+        this.backHandle()
+      }else{
+        this.code = this.$route.query.code
+        this.getDetailHandle()
+      }
+    },
+    mounted() {
+      window.addEventListener('beforeunload',this.markFinishStatus)
+    },
+    beforeDestroy(){
+      window.removeEventListener('beforeunload',this.markFinishStatus)
+    },
+    methods:{
+      /* 获取表格详情 */
+      getDetailHandle() {
+
+        this.isSheetLoading = true;
+        sheetInterface.sheetAnalysisInterface.getExcelDetail({
+          UniqueCode:this.code,
+        }).then((res) => {
+          if (res.Ret !== 200) return;
+          this.isCanEdit = res.Data.ExcelInfo.CanEdit
+          if(!this.isCanEdit){
+            this.$message.warning(`${res.Data.ExcelInfo.Editor}正在编辑中`)
+            setTimeout(()=>{
+              this.backHandle()
+            },1000)
+            return 
+          }
+          this.sheetDetailInfo = res.Data.ExcelInfo;
+          this.updateTime = this.$moment(this.sheetDetailInfo.ModifyTime).format('YYYY-MM-DD HH:mm:ss')||''
+          this.sheetId = this.sheetId || res.Data.ExcelInfo.ExcelInfoId;
+          this.dataToalPage =  Math.max(...res.Data.SheetList.map(_ => _.PageNum));
+          this.sheetAllcellData = res.Data.SheetList.map(_ => _.Data ? JSON.parse(_.Data.Data) : []);
+
+          this.getCellData(res.Data.SheetList)
+        });
+      },
+      //分页获取表格数据
+      async getCellData(sheets) {
+
+        let res = await sheetInterface.sheetAnalysisInterface.getExcelDataByPage({
+          UniqueCode: this.code,
+          Page: this.sheetDataPage
+        })
+
+        if(res.Ret !== 200) return
+        console.log(this.sheetAllcellData)
+
+        for(let i = 0;i<this.sheetAllcellData.length;i++) {
+          if(res.Data[i].Data) {
+            this.sheetAllcellData[i] = [...this.sheetAllcellData[i],...JSON.parse(res.Data[i].Data.Data)]
+          }
+
+          continue
+        }
+        
+        //数据继续加载或渲染表格.
+        if(this.sheetDataPage < this.dataToalPage) {
+          this.sheetDataPage++;
+          this.getCellData(sheets)
+        }else {
+          this.sheetConfigOpt.data = sheets.map((_,index) => ({
+            index: _.Index, //工作表id
+            order: _.Sort, //工作表的下标
+            name: _.SheetName,
+            calcChain: _.CalcChain?JSON.parse(_.CalcChain):[],
+            config: JSON.parse(_.Config),
+            celldata: this.sheetAllcellData[index],
+          }))
+
+          console.log(this.sheetConfigOpt)
+
+          this.isSheetLoading = false;
+        }
+      },
+      backHandle(){
+        let params={
+          code:this.sheetDetailInfo.UniqueCode,
+          id:this.sheetDetailInfo.ExcelInfoId
+        }
+        sessionStorage.setItem('editSheetAnalysisBack',JSON.stringify(params))
+        this.$router.back()
+      },
+      //跳转生成指标
+      HandleToPath() {
+        this.$router.push({ path: '/createTaregtBySheet',query: {
+          code: this.sheetDetailInfo.UniqueCode 
+        }});
+      },
+      /* 刷新表格 */
+      refreshSheet: _.debounce(async function() {
+        let res = await sheetInterface.sheetAnalysisInterface.sheetRefresh({ExcelInfoId: this.sheetDetailInfo.ExcelInfoId})
+
+        if(res.Ret !== 200) return 
+        this.$message.success(res.Msg)
+      },300),
+      /* 保存表格 */
+      saveHandle: _.debounce(async function () {
+        luckysheet.exitEditMode();
+        let data = luckysheet.getAllSheets();
+
+        this.loading = this.$loading({
+          target: ".sheet-detail-wrapper",
+          lock: true,
+          text: "保存中...",
+          spinner: "el-icon-loading",
+          background: "rgba(255, 255, 255, 0.6)",
+        });
+
+        let img = getSheetImage(data[0]);
+        const form = new FormData();
+        form.append("Image", img);
+        const { Data } = await sheetInterface.uploadImg(form);
+
+        data.luckysheet_select_save = [];
+        const { ExcelInfoId, ExcelName, ExcelClassifyId } = this.sheetDetailInfo;
+        const res = await sheetInterface.sheetAnalysisInterface.sheetEdit({
+          ExcelInfoId,
+          ExcelName,
+          ExcelClassifyId,
+          ExcelImage: Data.ResourceUrl,
+          Content: JSON.stringify(data),
+        });
+        this.loading.close();
+        if (res.Ret !== 200) return;
+        this.$message.success("保存成功");
+        this.$router.back()
+      }, 300),
+      markFinishStatus(){
+        if((!this.sheetId) || (!this.isCanEdit)) return
+        sheetInterface.markSheetEditStatus({ExcelInfoId: +this.sheetId,Status:2}).then(res=>{
+          if(res.Ret != 200) return 
+        })
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .sheet-detail-wrapper {
+    height: 100%;
+    border: 1px solid #ececec;
+    border-radius: 4px;
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.05);
+    overflow: auto;
+    background: #fff;
+    .detail-top {
+      padding: 20px;
+      // display: flex;
+      // justify-content: space-between;
+      // align-items: center;
+      border-bottom: 1px solid #ececec;
+      .sheet-name {
+        font-size: 18px;
+        color: #333333;
+        margin-bottom: 6px;
+        cursor: pointer;
+        max-width: 450px;
+        &:hover {
+          text-decoration: underline;
+        }
+      }
+      .sheet-anothor-info{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .author{
+          color: #666666;
+          font-size: 16px;
+        }
+        .action-ul {
+          display: flex;
+          li {
+            margin: 0 10px;
+            display: flex;
+            align-items: center;
+            img{
+              height: 16px;
+              width: 16px;
+              margin-right: 4px;
+            }
+          }
+        }
+      }
+    }
+    .sheet-wrap {
+      position: relative;
+      height: calc(100vh - 210px);
+    }
+  }
+</style>

+ 147 - 94
src/views/datasheet_manage/customAnalysis/list.vue

@@ -142,10 +142,7 @@
         <!-- 表格详情 -->
         <div class="sheet-detail-wrapper" v-if="select_id" >
           <div class="detail-top">
-            <span class="author"
-              >作者:{{ sheetDetailInfo.SysUserRealName }}</span
-            >
-            <el-input
+            <!-- <el-input
               ref="sheetEditTitRef"
               style="width: 400px"
               placeholder="请输入表格名称"
@@ -153,44 +150,60 @@
               v-model="sheet_title"
               v-if="sheetDetailInfo.isEditTit"
               @blur="changeValue(sheetDetailInfo, 'edit-tit')"
-            />
-            <span
-              class="sheet-name"
-              @click="editNodeLabel(sheetDetailInfo, 'edit-tit')"
-              v-else
-            >
+            /> -->
+            <div class="sheet-name">
               {{ sheetDetailInfo.ExcelName }}
-              <i class="el-icon-edit"/>
-            </span>
-            <ul class="action-ul" v-if="sheetDetailInfo.Button">
-
-              <el-tooltip effect="dark" content="在当前表格选择日期列和数据列生成指标" placement="top-start">
-                  <li class="editsty" @click="HandleToPath" v-if="isSheetBtnShow('createedb')&&sheetDetailInfo.Button.OpEdbButton">生成指标</li>
-              </el-tooltip>
-
-              <el-tooltip effect="dark" content="根据表格保存的最新内容,更新当前表格生成的所有指标" placement="top-start">
-                  <li class="editsty" @click="refreshSheet" v-if="isSheetBtnShow('refresh')&&sheetDetailInfo.Button.RefreshEdbButton">刷新指标</li>
-              </el-tooltip>
-              <li class="editsty" @click="saveHandle" v-if="isSheetBtnShow('save')&&sheetDetailInfo.Button.OpButton">保存</li>
-              <li
-                class="editsty"
-                @click="saveOtherHandle"
-                v-if="isSheetBtnShow('otherSave')&&sheetDetailInfo.Button.CopyButton"
-              >
-                另存为
-              </li>
-              <li class="editsty" @click="downloadExcel
-              (sheetDetailInfo)" v-if="isSheetBtnShow('download')&&sheetDetailInfo.Button.DownloadButton">
-                下载
-              </li>
-              <li
-                class="deletesty"
-                v-if="isSheetBtnShow('del')&&sheetDetailInfo.Button.DeleteButton"
-                @click="delSheetHandle({cell:sheetDetailInfo, type:'del'})"
-              >
-                删除
-              </li>
-            </ul>
+              <!-- <i class="el-icon-edit"/> -->
+            </div>
+            <div class="sheet-anothor-info">
+              <span class="author">作者:{{ sheetDetailInfo.SysUserRealName }}</span>
+              <ul class="action-ul" v-if="sheetDetailInfo.Button">
+                <li style="color:#999999 ;">最近保存时间:{{ saveTime }}</li>
+                <el-tooltip effect="dark" content="在当前表格选择日期列和数据列生成指标" placement="top-start">
+                    <li class="editsty" @click="HandleToPath" v-if="isSheetBtnShow('createedb')&&sheetDetailInfo.Button.OpEdbButton">
+                      <img src="~@/assets/img/icons/choose_bluebg_new.png"/>
+                      <span>生成指标</span>
+                    </li>
+                </el-tooltip>
+
+                <el-tooltip effect="dark" content="根据表格保存的最新内容,更新当前表格生成的所有指标" placement="top-start">
+                    <li class="editsty" @click="refreshSheet" v-if="isSheetBtnShow('refresh')&&sheetDetailInfo.Button.RefreshEdbButton">
+                      <img src="~@/assets/img/icons/refresh_blue_new.png"/>
+                      <span>刷新指标</span>
+                    </li>
+                </el-tooltip>
+                <!-- <li class="editsty" @click="saveHandle" v-if="isSheetBtnShow('save')&&sheetDetailInfo.Button.OpButton">保存</li> -->
+                <li
+                  class="editsty"
+                  @click="goEdit"
+                  v-if="(sheetDetailInfo.Button && sheetDetailInfo.Button.OpButton&&isSheetBtnShow('edit'))"
+                >
+                  <img src="~@/assets/img/icons/edit_blue_new.png" v-if="!editButtonText"/>
+                  <span>{{ editButtonText?editButtonText:'编辑' }}</span> 
+                </li>
+                <li
+                  class="editsty"
+                  @click="saveOtherHandle"
+                  v-if="isSheetBtnShow('otherSave')&&sheetDetailInfo.Button.CopyButton"
+                >
+                  <img src="~@/assets/img/icons/save_as_blue_new.png"/>
+                  <span>另存为</span>
+                </li>
+                <li class="editsty" @click="downloadExcel
+                (sheetDetailInfo)" v-if="isSheetBtnShow('download')&&sheetDetailInfo.Button.DownloadButton">
+                  <img src="~@/assets/img/icons/download_blue.png"/>
+                  <span>下载</span>
+                </li>
+                <li
+                  class="deletesty"
+                  v-if="(isSheetBtnShow('del')&&sheetDetailInfo.Button&&sheetDetailInfo.Button.DeleteButton)"
+                  @click="delSheetHandle({cell:sheetDetailInfo, type:'del'})"
+                >
+                  <img src="~@/assets/img/icons/delete-red.png"/>
+                  <span>删除</span>
+                </li>
+              </ul>
+            </div>
           </div>
 
           <!-- <dataLoading :loading="isSheetLoading"/> -->
@@ -201,12 +214,7 @@
               ref="sheetRef"
               v-if="sheetConfigOpt.data"
               :option="sheetConfigOpt"
-              :sheetInfo="{
-                ExcelInfoId: sheetDetailInfo.ExcelInfoId,
-                ExcelName: sheetDetailInfo.ExcelName,
-                ExcelClassifyId: sheetDetailInfo.ExcelClassifyId,
-                Source: sheetDetailInfo.Source
-              }"
+              :limit="{disabled:true}"
             />
           </div>
         </div>
@@ -316,7 +324,7 @@ export default {
       }));
 
       return options;
-    },
+    }
   },
   data() {
     return {
@@ -380,6 +388,10 @@ export default {
       sourceMap: {
         '/sheetAnalysisList': 4,
       },
+      saveTime:"",
+      editButtonText:'',
+      adminId:localStorage.getItem("AdminId") || '0',
+      excelAdminId:""
     };
   },
   watch: {
@@ -600,39 +612,54 @@ export default {
       };
       request.send();
     },
+    goEdit(){
+      sheetInterface.markSheetEditStatus({ExcelInfoId: this.select_id,Status:1}).then(res=>{
+        if(res.Ret != 200) return 
+        if(res.Data.Status==0){
+          this.$router.push({
+            path: '/editSheetAnalysis',
+            query: { code: this.sheetDetailInfo.UniqueCode },
+          });
 
+        }else if(res.Data.Status==1){
+          this.editButtonText = `${res.Data.Editor}编辑中`
+          this.$message.warning('当前'+this.editButtonText)
+        }
+      })
+    },
     /* 保存表格 */
-    saveHandle: _.debounce(async function () {
-      luckysheet.exitEditMode();
-      let data = luckysheet.getAllSheets();
-
-      this.loading = this.$loading({
-        target: ".dataSheet-container",
-        lock: true,
-        text: "保存中...",
-        spinner: "el-icon-loading",
-        background: "rgba(255, 255, 255, 0.6)",
-      });
-
-      let img = getSheetImage(data[0]);
-      const form = new FormData();
-      form.append("Image", img);
-      const { Data } = await sheetInterface.uploadImg(form);
-
-      data.luckysheet_select_save = [];
-      const { ExcelInfoId, ExcelName, ExcelClassifyId } = this.sheetDetailInfo;
-      const res = await sheetInterface.sheetAnalysisInterface.sheetEdit({
-        ExcelInfoId,
-        ExcelName,
-        ExcelClassifyId,
-        ExcelImage: Data.ResourceUrl,
-        Content: JSON.stringify(data),
-      });
-      this.loading.close();
-      if (res.Ret !== 200) return;
-      this.$message.success("保存成功");
-      this.getTreeData();
-    }, 300),
+    // saveHandle: _.debounce(async function () {
+    //   luckysheet.exitEditMode();
+    //   let data = luckysheet.getAllSheets();
+
+    //   this.loading = this.$loading({
+    //     target: ".dataSheet-container",
+    //     lock: true,
+    //     text: "保存中...",
+    //     spinner: "el-icon-loading",
+    //     background: "rgba(255, 255, 255, 0.6)",
+    //   });
+
+    //   let img = getSheetImage(data[0]);
+    //   const form = new FormData();
+    //   form.append("Image", img);
+    //   const { Data } = await sheetInterface.uploadImg(form);
+
+    //   data.luckysheet_select_save = [];
+    //   const { ExcelInfoId, ExcelName, ExcelClassifyId } = this.sheetDetailInfo;
+
+    //   const res = await sheetInterface.sheetAnalysisInterface.sheetEdit({
+    //     ExcelInfoId,
+    //     ExcelName,
+    //     ExcelClassifyId,
+    //     ExcelImage: Data.ResourceUrl,
+    //     Content: JSON.stringify(data),
+    //   });
+    //   this.loading.close();
+    //   if (res.Ret !== 200) return;
+    //   this.$message.success("保存成功");
+    //   this.getTreeData();
+    // }, 300),
 
     /* 获取表格列表 */
     getPublicList() {
@@ -682,6 +709,9 @@ export default {
         if (res.Ret !== 200) return;
 
         this.sheetDetailInfo = res.Data.ExcelInfo;
+        this.saveTime = this.$moment(this.sheetDetailInfo.ModifyTime).format('YYYY-MM-DD HH:mm:ss')||''
+        this.excelAdminId = this.sheetDetailInfo.SysUserId
+        this.editButtonText = this.sheetDetailInfo.CanEdit?'':`${this.sheetDetailInfo.Editor}编辑中`
         this.dataToalPage =  Math.max(...res.Data.SheetList.map(_ => _.PageNum));
         this.sheetAllcellData = res.Data.SheetList.map(_ => _.Data ? JSON.parse(_.Data.Data) : []);
 
@@ -825,7 +855,11 @@ export default {
         code: this.$route.query.code,
         id: Number(this.$route.query.id),
       });
-    } else {
+    } else if(sessionStorage.getItem('editSheetAnalysisBack')){
+      let params=JSON.parse(sessionStorage.getItem('editSheetAnalysisBack'))
+      this.getTreeData(params);
+      sessionStorage.removeItem('editSheetAnalysisBack')
+    }else {
       this.getTreeData();
       this.getPublicList();
     }
@@ -870,7 +904,7 @@ $normal-font: 14px;
 
     .main-left {
       width: 400px;
-      min-width: 350px;
+      min-width: 300px;
       background: #fff;
       margin-right: 20px;
       border: 1px solid #ececec;
@@ -940,6 +974,7 @@ $normal-font: 14px;
 
     .main-right {
       width: 80%;
+      min-width: 900px;
       position: relative;
       .sheet-detail-wrapper {
         height: 100%;
@@ -950,28 +985,46 @@ $normal-font: 14px;
         background: #fff;
         .detail-top {
           padding: 20px;
-          display: flex;
-          justify-content: space-between;
-          align-items: center;
+          // display: flex;
+          // justify-content: space-between;
+          // align-items: center;
           border-bottom: 1px solid #ececec;
           .sheet-name {
-            font-size: 17px;
-            cursor: pointer;
-            max-width: 450px;
-            &:hover {
-              text-decoration: underline;
-            }
+            font-size: 18px;
+            color: #333333;
+            margin-bottom: 6px;
+            // cursor: pointer;
+            // max-width: 450px;
+            // &:hover {
+            //   text-decoration: underline;
+            // }
           }
-          .action-ul {
+          .sheet-anothor-info{
             display: flex;
-            li {
-              margin: 0 10px;
+            justify-content: space-between;
+            align-items: center;
+            .author{
+              color: #666666;
+              font-size: 16px;
+            }
+            .action-ul {
+              display: flex;
+              li {
+                margin: 0 10px;
+                display: flex;
+                align-items: center;
+                img{
+                  height: 16px;
+                  width: 16px;
+                  margin-right: 4px;
+                }
+              }
             }
           }
         }
         .sheet-wrap {
           position: relative;
-          height: calc(100vh - 190px);
+          height: calc(100vh - 210px);
           padding: 15px;
           /* min-height: 500px; */
         }

+ 196 - 78
src/views/datasheet_manage/customSheetEdit.vue

@@ -1,72 +1,79 @@
 <template>
   <div class="customSheet-wrap">
     <div class="wrap-top">
-      <ul class="form-ul">
-        <li>
-          <selectTarget
-            ref="selectRef"
-            @select="handleSelectTarget"
-            :selectStyleType="2"
-          />
-        </li>
-        <li>
-          <el-input
-            v-model="sheetForm.name"
-            placeholder="请输入表格名称"
-            style="width:200px"
-            clearable>
-          </el-input>
-        </li>
-        <li>
-          <el-select 
-            v-model="sheetForm.classify" 
-            placeholder="请选择表格分类"
-            clearable
-          >
-							<el-option
-								v-for="item in classifyArr"
-								:key="item.ExcelClassifyId"
-								:label="item.ExcelClassifyName"
-								:value="item.ExcelClassifyId"
-              />
-						</el-select>
-        </li>
-        <li>
-          <el-select 
-            v-model="sheetForm.sheetType"
-            style="width:150px;"
-          >
-							<el-option
-								v-for="item in sheetTypeOption"
-								:key="item.key"
-								:label="item.label"
-								:value="item.key"
-              />
-						</el-select>
-        </li>
-        <li>
-          表格说明
-          <el-tooltip
-            effect="dark"
-          >
-            <div
-              slot="content"
-              v-html="rules"
-              style="line-height: 20px;width:350px"
-            ></div>
-            <i class="el-icon-question" />
-          </el-tooltip>
-        </li>
-      </ul>
-      <div>
-        <el-button type="primary" size="medium" @click="saveSheetHandle">保存</el-button>
-        <el-button type="primary" size="medium" plain @click="backHandle">返回</el-button>
+      <div class="database-select">
+        <selectTarget
+          ref="selectRef"
+          @select="handleSelectTarget"
+          :selectStyleType="2"
+        />
+        <div>
+          <div v-if="saveTime" style="color:#999999;margin-right:30px ;">
+            最近保存时间:{{ saveTime }}
+          </div>
+          <el-button type="primary" size="medium" @click="saveSheetHandle" v-if="hasPermission">保存</el-button>
+          <el-button type="primary" size="medium" plain @click="backHandle">返回</el-button>
+        </div>
+      </div>
+      <div class="wrap-top-bottom">
+        <ul class="form-ul">
+          <li>
+            <el-input
+              v-model="sheetForm.name"
+              placeholder="请输入表格名称"
+              style="width:240px"
+              clearable>
+            </el-input>
+          </li>
+          <li>
+            <el-select 
+              v-model="sheetForm.classify" 
+              placeholder="请选择表格分类"
+              style="width:240px;"
+              clearable
+            >
+                <el-option
+                  v-for="item in classifyArr"
+                  :key="item.ExcelClassifyId"
+                  :label="item.ExcelClassifyName"
+                  :value="item.ExcelClassifyId"
+                />
+              </el-select>
+          </li>
+          <li>
+            <el-select 
+              v-model="sheetForm.sheetType"
+              style="width:240px;"
+            >
+                <el-option
+                  v-for="item in sheetTypeOption"
+                  :key="item.key"
+                  :label="item.label"
+                  :value="item.key"
+                />
+              </el-select>
+          </li>
+          <li>
+            表格说明
+            <el-tooltip
+              effect="dark"
+            >
+              <div
+                slot="content"
+                v-html="rules"
+                style="line-height: 20px;width:350px"
+              ></div>
+              <i class="el-icon-question" />
+            </el-tooltip>
+          </li>
+        </ul>
       </div>
     </div>
     
     <CustomTable
       :sheetType="sheetForm.sheetType"
       ref="customTableRef"
+      @autoSave="autoSaveFun"
     />
   </div>
 </template>
@@ -87,6 +94,30 @@ export default {
     }
     next()
   },
+  watch:{
+    sheetForm:{
+      handler(newVal){
+        console.log(newVal,'newVal','newVal');
+        if(this.sheetInit && this.sheetId) this.autoSaveFun()
+        
+      },
+      deep:true
+    }
+  },
+  computed:{
+    hasPermission(){
+      // console.log(this.sheetButton,'sheetButton');
+      return this.sheetButton?
+            this.permissionBtn.isShowBtn('etaTablePermission','etaTable_customize_data_save')&&this.sheetButton.OpButton:
+            this.permissionBtn.isShowBtn('etaTablePermission','etaTable_customize_data_save')
+    }
+  },
+  beforeRouteLeave(to,from,next){
+    if(to.path!='/addMixedSheet'){
+      this.markFinishStatus()
+    }
+    next()
+  },
   data() {
     return {
       sheetId: this.$route.query.id || '',
@@ -100,13 +131,19 @@ export default {
         { key: 1,label: '指标列+日期行' },
         { key: 2,label: '指标行+日期列' },
       ],
-
+      saveTime:"",
+      sheetInit:false,
+      isCanEdit:false,
+      sheetButton:'',
+      // 取消自动保存,比如返回的时候
+      cancelAutoSave:false
     }
   },
   methods: {
 
     backHandle() {
-      this.$router.go(-1)
+      this.$router.back()
+      this.cancelAutoSave=true
     },
 
     /* 获取表格详情 */
@@ -118,14 +155,26 @@ export default {
 			})
 
       if(res.Ret !== 200)  return
-      const { ExcelName,ExcelClassifyId,ExcelType,TableData } = res.Data;
-      
+      this.isCanEdit = res.Data.CanEdit
+      if(!res.Data.CanEdit){
+        this.$message.warning(`${res.Data.Editor}正在编辑中`)
+        setTimeout(()=>{
+          this.backHandle()
+        },1000)
+        return 
+      }
+      const { ExcelName,ExcelClassifyId,ExcelType,TableData,ModifyTime,Button} = res.Data;
+      this.sheetButton=Button
       this.sheetForm = {
         name: ExcelName,
         classify: ExcelClassifyId,
         sheetType: ExcelType
       }
+      this.saveTime =  this.$moment(ModifyTime).format('YYYY-MM-DD HH:mm:ss')
 
+      this.$nextTick(()=>{
+        this.sheetInit=true
+      })
       this.$refs.customTableRef.initSheetData(TableData);
     },
 
@@ -169,7 +218,25 @@ export default {
       })
     },
 
-
+    autoSaveFun:_.debounce(async function(){
+      // console.log("触发自动",this.cancelAutoSave);
+      if(!this.sheetId || this.cancelAutoSave) return 
+      const { name,classify,sheetType } = this.sheetForm;
+      if(!name || !classify) return this.$message.warning(name ? '请选择表格分类' : '请输入表格名称')
+      if(!document.getElementsByClassName('table')[0]) return this.$message.warning('请添加表格')
+      let params = {
+        ExcelName: name,
+        ExcelType: sheetType,
+        ExcelClassifyId: classify,
+        ExcelImage:'',
+        Source: 3,
+        TableData: this.$refs.customTableRef.getSaveParams()
+      };
+      console.log("自动保存");
+      const res = await sheetInterface.sheetEdit({ ExcelInfoId: Number(this.sheetId),...params })
+      if(res.Ret !==200) return
+      this.saveTime = this.$moment().format('YYYY-MM-DD HH:mm:ss')
+    },1500),
     /* 保存表格 */
     saveSheetHandle: _.debounce(async function() {
       const { name,classify,sheetType } = this.sheetForm;
@@ -200,15 +267,20 @@ export default {
         TableData: this.$refs.customTableRef.getSaveParams()
       };
 
+      let isAdd = this.sheetId?false:true
+
       const res = this.sheetId
       ? await sheetInterface.sheetEdit({ ExcelInfoId: Number(this.sheetId),...params })
       : await sheetInterface.sheetAdd(params)
 
       if(res.Ret !==200) return
-      
+
       this.sheetId = this.sheetId || res.Data.ExcelInfoId;
       this.$message.success('保存成功')
+      this.saveTime =  this.$moment().format('YYYY-MM-DD HH:mm:ss')
       
+      isAdd && this.$router.replace({path:'/addCustomSheet',query:{id:this.sheetId}})
+
       // this.$router.replace({
       //   path: '/sheetList',
       //   query: {
@@ -217,11 +289,22 @@ export default {
       //   }
       // })
     },300),
-    
+    markFinishStatus(){
+      if((!this.sheetId) || (!this.isCanEdit)) return
+      sheetInterface.markSheetEditStatus({ExcelInfoId: +this.sheetId,Status:2}).then(res=>{
+        if(res.Ret != 200) return 
+      })
+    }
   },
   created() {
     this.getClassify();
     this.getDetail();
+  },
+  mounted(){
+    window.addEventListener('beforeunload',this.markFinishStatus)
+  },
+  beforeDestroy(){
+    window.removeEventListener('beforeunload',this.markFinishStatus)
   }
 }
 </script>
@@ -229,30 +312,65 @@ export default {
 *{ box-sizing: border-box; }
 .customSheet-wrap {
   min-height: calc(100vh - 120px);
+  min-width: 1020px;
   .wrap-top {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
+    // display: flex;
+    // justify-content: space-between;
+    // align-items: center;
     margin-bottom: 20px;
     padding: 20px;
     background: #fff;
     border: 1px solid #ececec;
     border-radius: 4px;
     box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
-    display: flex;
+    // display: flex;
     z-index: 1;
-    .form-ul {
-      flex: 1;
+    .database-select{
       display: flex;
       align-items: center;
-      li {
-        margin: 0 10px;
-        padding-top: 40px;
-        &:first-child {
-          padding-top: 0;
+      margin-bottom: 16px;
+      div{
+        display: flex;
+        align-items: center;
+      }
+    }
+    .wrap-top-bottom{
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      .form-ul {
+        flex: 1;
+        display: flex;
+        align-items: center;
+        li {
+          margin: 0 10px;
+          // padding-top: 40px;
+          &:first-child {
+            padding-top: 0;
+            margin:0
+          }
         }
       }
     }
+
   }
 }
+</style>
+<style lang="scss">
+  .database-select{
+    .el-select{
+      margin-top: 0!important;
+      margin-left: 20px;
+      .el-input{
+        width: 240px;
+      }
+    }
+    .database-choose{
+      display: flex;
+      align-items: center;
+      label,div{
+        white-space: nowrap;
+      }
+    }
+  }
 </style>

+ 88 - 14
src/views/datasheet_manage/mixedSheetEdit.vue

@@ -6,7 +6,7 @@
           <el-input
             v-model="sheetForm.name"
             placeholder="请输入表格名称"
-            style="width:200px"
+            style="width:240px"
             clearable>
           </el-input>
         </li>
@@ -15,6 +15,7 @@
             v-model="sheetForm.classify" 
             placeholder="请选择表格分类"
             clearable
+            style="min-width:240px"
           >
 							<el-option
 								v-for="item in classifyArr"
@@ -39,13 +40,13 @@
         </li>
       </ul>
       <div>
-        <span v-if="updateTime">上次保存时间:{{updateTime}}</span>
-        <el-button type="primary" size="medium" @click="saveSheetHandle" style="margin-left:10px">保存</el-button>
+        <span v-if="updateTime" style="color:#999999 ;">最近保存时间:{{updateTime}}</span>
+        <el-button type="primary" size="medium" @click="saveSheetHandle" style="margin-left:10px" v-if="hasPermission">保存</el-button>
         <el-button type="primary" size="medium" plain @click="backHandle">返回</el-button>
       </div>
     </div>
     
-    <MixedTable
+    <MixedTable @autoSave="autoSaveFun"
       ref="mixedTableRef"
     />
   </div>
@@ -67,6 +68,30 @@ export default {
     }
     next()
   },
+  watch:{
+    sheetForm:{
+      handler(newVal){
+        // console.log(newVal,'newVal','newVal');
+        if(this.sheetInit && this.sheetId) this.autoSaveFun()
+        
+      },
+      deep:true
+    }
+  },
+  computed:{
+    hasPermission(){
+      // console.log(this.sheetButton,'sheetButton');
+      return this.sheetButton?
+              this.permissionBtn.isShowBtn('etaTablePermission','etaTable_customize_mix_save')&&this.sheetButton.OpButton:
+              this.permissionBtn.isShowBtn('etaTablePermission','etaTable_customize_mix_save')
+    }
+  },
+  beforeRouteLeave(to,from,next){
+    if(to.path!='/addMixedSheet'){
+      this.markFinishStatus()
+    }
+    next()
+  },
   data() {
     return {
       sheetId: this.$route.query.id || '',
@@ -78,19 +103,24 @@ export default {
       sheetForm: {
         sheetType: 1
       },
+      sheetButton:'',
+      sheetInit:false,
       sheetTypeOption: [
         { key: 1,label: '指标列+日期行' },
         { key: 2,label: '指标行+日期列' },
       ],
 
-      updateTime: ''
-
+      updateTime: '',
+      isCanEdit:false,
+      // 取消自动保存,比如返回的时候
+      cancelAutoSave:false
     }
   },
   methods: {
 
     backHandle() {
-      this.$router.go(-1)
+      this.cancelAutoSave=true
+      this.$router.back()
     },
 
     /* 获取表格详情 */
@@ -102,12 +132,24 @@ export default {
 			})
 
       if(res.Ret !== 200)  return
-      const { ExcelName,ExcelClassifyId,TableData,ModifyTime } = res.Data;
-      
+      this.isCanEdit = res.Data.CanEdit
+      if(!res.Data.CanEdit){
+        this.$message.warning(`${res.Data.Editor}正在编辑中`)
+        setTimeout(()=>{
+          this.backHandle()
+        },2000)
+        return 
+      }
+
+      const { ExcelName,ExcelClassifyId,TableData,ModifyTime,Button } = res.Data;
+      this.sheetButton=Button
       this.sheetForm = {
         name: ExcelName,
         classify: ExcelClassifyId
       }
+      this.$nextTick(()=>{
+        this.sheetInit=true
+      })
       this.updateTime =  this.$moment(ModifyTime).format('YYYY-MM-DD HH:mm:ss')
 
       this.$refs.mixedTableRef.initData(TableData);
@@ -121,8 +163,28 @@ export default {
         this.classifyArr = res.Data.AllNodes || [];
       })
     },
-
-
+    autoSaveFun:_.debounce(async function(){
+      if(!this.sheetId || this.cancelAutoSave) return 
+      const { name,classify,sheetType } = this.sheetForm;
+      if(!name || !classify) return this.$message.warning(name ? '请选择表格分类' : '请输入表格名称')
+      let checkAllEmpty = this.$refs.mixedTableRef.config.data.flat(1).some(_ => _.ShowValue);
+      if(!checkAllEmpty) return this.$message.warning('请输入表格内容')
+      let params = {
+        ExcelName: name,
+        ExcelType: 1,
+        ExcelClassifyId: classify,
+        ExcelImage:'',
+        Source: 3,
+        TableData: {
+          CellRelation: JSON.stringify(this.$refs.mixedTableRef.insertRelationArr),
+          Data: this.$refs.mixedTableRef.config.data
+        }
+      };
+      console.log("自动保存");
+      const res = await sheetInterface.sheetEdit({ ExcelInfoId: Number(this.sheetId),...params })
+      if(res.Ret !==200) return
+      this.updateTime = this.$moment().format('YYYY-MM-DD HH:mm:ss')
+    },1500),
     /* 保存表格 */
     saveSheetHandle: _.debounce(async function() {
 
@@ -156,7 +218,7 @@ export default {
           Data: this.$refs.mixedTableRef.config.data
         }
       };
-
+      let isAdd = this.sheetId?false:true
       const res = this.sheetId
       ? await sheetInterface.sheetEdit({ ExcelInfoId: Number(this.sheetId),...params })
       : await sheetInterface.sheetAdd(params)
@@ -166,19 +228,31 @@ export default {
       
       this.sheetId = this.sheetId || res.Data.ExcelInfoId;
       this.$message.success('保存成功')
-
+      isAdd && this.$router.replace({path:'/addMixedSheet',query:{id:this.sheetId}})
     },300),
-    
+    markFinishStatus(){
+      if((!this.sheetId) || (!this.isCanEdit)) return
+      sheetInterface.markSheetEditStatus({ExcelInfoId: +this.sheetId,Status:2}).then(res=>{
+        if(res.Ret != 200) return 
+      })
+    }
   },
   created() {
     this.getClassify();
     this.getDetail();
+  },
+  mounted(){
+    window.addEventListener('beforeunload',this.markFinishStatus)
+  },
+  beforeDestroy(){
+    window.removeEventListener('beforeunload',this.markFinishStatus)
   }
 }
 </script>
 <style scoped lang="scss">
 *{ box-sizing: border-box; }
 .customSheet-wrap {
+  min-width: 1070px;
   min-height: calc(100vh - 120px);
   .wrap-top {
     display: flex;

+ 4 - 24
src/views/datasheet_manage/mixins/classifyMixin.js

@@ -110,6 +110,7 @@ export default {
         if(!this.sheet_title) return this.$message.warning('表格名称不能为空');
         data.isEditTit = false;
         data.ExcelName = this.sheet_title;
+        console.log(data,'data');
       }else {
         if(!this.new_label) return this.$message.warning('名称不能为空');
         this.$set(data,'isEdit',false)
@@ -289,36 +290,15 @@ export default {
 
       return canDrop;
     },
-
-    //判断右侧列表的下载按钮是否显示
-    isDownLoadShow(cell){
-        const {checkPermissionBtn,etaTablePermission} = this.permissionBtn
-        const checkMap = {
-            1:etaTablePermission.etaTable_excel_download,
-            2:etaTablePermission.etaTable_customize_data_download,
-            3:etaTablePermission.etaTable_customize_mix_download,
-            4:etaTablePermission.etaTable_analysis_download
-        }
-        return checkPermissionBtn(checkMap[cell.Source])
-    },
-    //判断右侧列表的删除按钮是否显示
-    isDeleteShow(cell){
-        const {checkPermissionBtn,etaTablePermission} = this.permissionBtn
-        const checkMap = {
-            1:etaTablePermission.etaTable_excel_del,
-            2:etaTablePermission.etaTable_customize_data_del,
-            3:etaTablePermission.etaTable_customize_mix_del,
-            4:etaTablePermission.etaTable_analysis_del,
-        }
-        return checkPermissionBtn(checkMap[cell.Source])
-    },
+    
     //判断自定义表格-编辑,另存为,刷新按钮是否显示
     isSheetBtnShow(type){
       const sheetType = {
         '/sheetList': 'etaTable_excel',
         '/sheetTimeList': 'etaTable_customize_data',
         '/sheetMixedList': 'etaTable_customize_mix',
-        '/sheetAnalysisList': 'etaTable_analysis'
+        '/sheetAnalysisList': 'etaTable_analysis',
+        '/editSheetAnalysis': 'etaTable_analysis'
       }
       return this.permissionBtn.isShowBtn('etaTablePermission',`${sheetType[this.$route.path]}_${type}`)
     }

+ 121 - 94
src/views/datasheet_manage/sheetList.vue

@@ -136,10 +136,7 @@
         <!-- 表格详情 -->
         <div class="sheet-detail-wrapper" v-if="select_id">
           <div class="detail-top">
-            <span class="author"
-              >作者:{{ sheetDetailInfo.SysUserRealName }}</span
-            >
-            <el-input
+            <!-- <el-input
               ref="sheetEditTitRef"
               style="width: 400px"
               placeholder="请输入表格名称"
@@ -147,64 +144,66 @@
               v-model="sheet_title"
               v-if="sheetDetailInfo.isEditTit"
               @blur="changeValue(sheetDetailInfo, 'edit-tit')"
-            />
-            <span
+            /> -->
+            <!-- <span
               class="sheet-name"
               @click="editNodeLabel(sheetDetailInfo, 'edit-tit')"
               v-else
             >
               {{ sheetDetailInfo.ExcelName }}
               <i class="el-icon-edit" v-if="sheetDetailInfo.Source === 1" />
-            </span>
-            <ul class="action-ul">
-              <li
-                class="editsty"
-                @click="saveHandle"
-                v-if="
-                  sheetDetailInfo.Source === 1 &&
-                  sheetDetailInfo.Button.OpButton&&isSheetBtnShow('save')
-                "
-              >
-                保存
-              </li>
-              <template v-if="[2, 3].includes(sheetDetailInfo.Source)">
+            </span> -->
+            <div class="sheet-name">
+              {{ sheetDetailInfo.ExcelName }}
+            </div>
+            <div class="sheet-anothor-info">
+              <span class="author">作者:{{ sheetDetailInfo.SysUserRealName }}</span>
+              <ul class="action-ul">
+                <li style="color:#999999 ;">最近保存时间:{{ saveTime }}</li>
                 <li
                   class="editsty"
                   @click="goEditHandle"
-                  v-if="sheetDetailInfo.Button.OpButton&&isSheetBtnShow('edit')"
+                  v-if="(sheetDetailInfo.Button && sheetDetailInfo.Button.OpButton&&isSheetBtnShow('edit'))"
                 >
-                  编辑
+                  <img src="~@/assets/img/icons/edit_blue_new.png" v-if="!editButtonText"/>
+                  <span>{{ editButtonText?editButtonText:'编辑' }}</span> 
                 </li>
-                <li
-                  class="editsty"
-                  @click="refreshSheetEdb"
-                  v-if="sheetDetailInfo.Button.RefreshButton&&isSheetBtnShow('refresh')"
-                >
-                  刷新
+                <template v-if="[2, 3].includes(sheetDetailInfo.Source)">
+                  <li
+                    class="editsty"
+                    @click="refreshSheetEdb"
+                    v-if="sheetDetailInfo.Button.RefreshButton&&isSheetBtnShow('refresh')"
+                  >
+                  <img src="~@/assets/img/icons/refresh_blue_new.png"/>
+                    <span>刷新</span>
+                  </li>
+                  <li
+                    class="editsty"
+                    @click="saveOtherHandle"
+                    v-if="sheetDetailInfo.Button.CopyButton&&isSheetBtnShow('otherSave')"
+                  >
+                    <img src="~@/assets/img/icons/save_as_blue_new.png"/>
+                    <span>另存为</span>
+                  </li>
+                </template>
+                <li v-if="isSheetBtnShow('download')"
+                  class="editsty" @click="downloadExcel(sheetDetailInfo)">
+                  <img src="~@/assets/img/icons/download_blue.png"/>
+                  <span>下载</span>
                 </li>
                 <li
-                  class="editsty"
-                  @click="saveOtherHandle"
-                  v-if="sheetDetailInfo.Button.CopyButton&&isSheetBtnShow('otherSave')"
+                  class="deletesty"
+                  v-if="(sheetDetailInfo.Button && sheetDetailInfo.Button.DeleteButton
+                    &&isSheetBtnShow('del'))
+                  "
+                  @click="delSheetHandle({cell:sheetDetailInfo, type:'del'})"
                 >
-                  另存为
+                  <img src="~@/assets/img/icons/delete-red.png"/>
+                  <span>删除</span>
                 </li>
-              </template>
-              <li v-if="isDownLoadShow(sheetDetailInfo)"
-                class="editsty" @click="downloadExcel(sheetDetailInfo)">
-                下载
-              </li>
-              <li
-                class="deletesty"
-                v-if="
-                  sheetDetailInfo.Button && sheetDetailInfo.Button.DeleteButton
-                  &&isDeleteShow(sheetDetailInfo)
-                "
-                @click="delSheetHandle({cell:sheetDetailInfo, type:'del'})"
-              >
-                删除
-              </li>
-            </ul>
+              </ul>
+            </div>
+
           </div>
 
           <!-- 表格 -->
@@ -219,12 +218,7 @@
                   scrollLeft: 0
                 }]
               }"
-              :sheetInfo="{
-                ExcelInfoId: sheetDetailInfo.ExcelInfoId,
-                ExcelName: sheetDetailInfo.ExcelName,
-                ExcelClassifyId: sheetDetailInfo.ExcelClassifyId,
-                Source: sheetDetailInfo.Source
-              }"
+              :limit="{disabled:true}"
             />
 
             <!-- 自定义表格  -->
@@ -347,19 +341,6 @@ export default {
         ExcelClassifyName: _.ExcelClassifyName,
       }));
       return options;
-    },
-
-     //数据表格是否展示
-    isShowDataSheet(){
-        const cell = {Source:2}
-        return this.isSheetBtnShow(cell,'edit')||this.isSheetBtnShow(cell,'refresh')||this.isSheetBtnShow(cell,'otherSave')
-            || this.isSheetBtnShow(cell,'download')||this.isSheetBtnShow(cell,'del')
-    },
-    //混合表格是否展示
-    isShowMixSheet(){
-        const cell = {Source:3}
-        return this.isSheetBtnShow(cell,'edit')||this.isSheetBtnShow(cell,'refresh')||this.isSheetBtnShow(cell,'otherSave')
-            || this.isSheetBtnShow(cell,'download')||this.isSheetBtnShow(cell,'del')
     }
   },
   data() {
@@ -410,12 +391,15 @@ export default {
           { required: true, message: "表格分类不能为空", trigger: "blur" },
         ],
       },
-
       sourceMap: {
         '/sheetList': 1,
         '/sheetTimeList': 2,
         '/sheetMixedList': 3,
-      }
+      },
+      saveTime:"",
+      editButtonText:"",
+      adminId:localStorage.getItem("AdminId") || '0',
+      excelAdminId:""
     };
   },
   watch: {
@@ -513,6 +497,7 @@ export default {
 
     /* 选中分类变化时 */
     nodeChange({ UniqueCode, ExcelInfoId, ExcelClassifyId }, node) {
+      console.log(this.select_id,ExcelInfoId,'UniqueCode');
       this.search_txt = "";
       this.select_node = UniqueCode;
       this.select_classify = !ExcelInfoId ? ExcelClassifyId : 0;
@@ -666,7 +651,8 @@ export default {
     /* 保存表格 */
     saveHandle: _.debounce(async function () {
       luckysheet.exitEditMode();
-      let data = luckysheet.getAllSheets()[0];
+      //结构类型乱飘 强制定义下
+      let data = {...luckysheet.getAllSheets()[0],status:Number(luckysheet.getAllSheets()[0].status)}
       if (!data.celldata.length) return this.$message.warning("请输入表格内容");
 
       this.loading = this.$loading({
@@ -737,16 +723,21 @@ export default {
 
     /* 获取表格详情 */
     getDetailHandle() {
-      sheetInterface
-        .sheetDetail({
+      sheetInterface.sheetDetail({
           ExcelInfoId: this.select_id,
         })
         .then((res) => {
           if (res.Ret !== 200) return;
 
           this.sheetDetailInfo = res.Data;
+          this.saveTime = this.$moment(this.sheetDetailInfo.ModifyTime).format('YYYY-MM-DD HH:mm:ss')||''
+          this.excelAdminId = this.sheetDetailInfo.SysUserId
+          this.editButtonText = this.sheetDetailInfo.CanEdit?'':`${this.sheetDetailInfo.Editor}编辑中`
+          //从nextTick里面拉出来 更多功能按钮 显示的出来 但是删除表格,请求下一个表格详情时,表格内容不会更新。
+          // this.sheetDetailInfo.Source === 1 && this.$refs.sheetRef && this.$refs.sheetRef.init();
 
           this.$nextTick(() => {
+
             this.sheetDetailInfo.Source === 1 && this.$refs.sheetRef.init();
 
             this.sheetDetailInfo.Source === 2 &&
@@ -771,17 +762,33 @@ export default {
         })
         .catch(() => {});
     },
-
     /* 编辑 */
     goEditHandle() {
-      let path = {
-        2: "/addCustomSheet",
-        3: "addMixedSheet",
-      };
-      this.$router.push({
-        path: path[this.sheetDetailInfo.Source],
-        query: { id: this.sheetDetailInfo.ExcelInfoId },
-      });
+      // 标记
+      sheetInterface.markSheetEditStatus({ExcelInfoId: this.select_id,Status:1}).then(res=>{
+        if(res.Ret != 200) return 
+        if(res.Data.Status==0){
+          let path = {
+            1:"/addsheet",
+            2: "/addCustomSheet",
+            3: "/addMixedSheet",
+          };
+          if(this.sheetDetailInfo.Source === 1) {
+            const { href } = this.$router.resolve({ path: path[this.sheetDetailInfo.Source],query: { id: this.sheetDetailInfo.ExcelInfoId } });
+            window.open(href, "_blank");
+          }else {
+            this.$router.push({
+              path: path[this.sheetDetailInfo.Source],
+              query: { id: this.sheetDetailInfo.ExcelInfoId },
+            });
+          }
+
+        }else if(res.Data.Status==1){
+          this.editButtonText = `${res.Data.Editor}编辑中`
+          this.$message.warning('当前'+this.editButtonText)
+        }
+      })
+
     },
 
     /* 刷新表格 */
@@ -886,7 +893,7 @@ $normal-font: 14px;
 
     .main-left {
       width: 400px;
-      min-width: 350px;
+      min-width: 300px;
       background: #fff;
       margin-right: 20px;
       border: 1px solid #ececec;
@@ -956,6 +963,7 @@ $normal-font: 14px;
 
     .main-right {
       width: 80%;
+      min-width: 800px;
       .sheet-detail-wrapper {
         height: 100%;
         border: 1px solid #ececec;
@@ -965,28 +973,47 @@ $normal-font: 14px;
         background: #fff;
         .detail-top {
           padding: 20px;
-          display: flex;
-          justify-content: space-between;
-          align-items: center;
+          // display: flex;
+          // justify-content: space-between;
+          // align-items: center;
           border-bottom: 1px solid #ececec;
           .sheet-name {
-            font-size: 17px;
-            cursor: pointer;
-            max-width: 450px;
-            &:hover {
-              text-decoration: underline;
-            }
+            font-size: 18px;
+            color: #333333;
+            margin-bottom: 6px;
+            // cursor: pointer;
+            // max-width: 450px;
+            // &:hover {
+            //   text-decoration: underline;
+            // }
           }
-          .action-ul {
+          .sheet-anothor-info{
             display: flex;
-            li {
-              margin: 0 10px;
+            justify-content: space-between;
+            align-items: center;
+            .author{
+              color: #666666;
+              font-size: 16px;
+            }
+            .action-ul {
+              display: flex;
+              li {
+                margin: 0 10px;
+                display: flex;
+                align-items: center;
+                img{
+                  height: 16px;
+                  width: 16px;
+                  margin-right: 4px;
+                }
+              }
             }
           }
+
         }
         .sheet-wrap {
           position: relative;
-          height: calc(100vh - 190px);
+          height: calc(100vh - 210px);
           padding: 15px;
           /* min-height: 500px; */
         }

+ 4 - 0
src/views/mychart_manage/components/chartDetailDia.vue

@@ -816,6 +816,10 @@ export default {
       this.tableData.forEach((item) => {
         let edbData = EdbInfoList.find(_ => _.EdbInfoId===item.EdbInfoId);
         item.DataList = edbData.DataList;
+        //更新起始时间和最近更新时间
+        item.StartDate = edbData.StartDate;
+        item.ModifyTime = edbData.ModifyTime;
+        
         if(edbData.EdbInfoCategoryType===1) item.MoveLatestDate = edbData.MoveLatestDate;
       });
     },

+ 2 - 2
src/views/ppt_manage/newVersion/components/Cover.vue

@@ -1,8 +1,8 @@
 <template>
-    <div class="flex-column cover" :style="`width:100%;color: ${$parent.pptCoverTextColor||'#fff'};`" v-if="pageInfo">
+    <div class="flex-column cover" :style="`width:100%;height:100%;color: ${$parent.pptCoverTextColor||'#fff'};`" v-if="pageInfo">
         <!-- <img :src="pageInfo.BackgroundImg" class="pptbg" /> -->
         <!-- <img :src="base64Url" class="pptbg" /> -->
-        <img :src="pageInfo.BackgroundImg" class="pptbg"  style="width:100%"/>
+        <img :src="pageInfo.BackgroundImg" class="pptbg"  style="width:100%;height:100%;object-fit: fill !important;"/>
         <div
         style="width:62%; font-size:16px; text-align:center; line-height:1.6;  position:absolute; right:20px; top:50%;zIndex:20;">
         <p :style="`height:5px; marginBottom:21px;`"></p>

+ 2 - 2
src/views/ppt_manage/newVersion/components/CoverEn.vue

@@ -1,6 +1,6 @@
 <template>
-    <div class="flex-column cover" style="width:100%;" v-if="pageInfo">
-        <img :src="pageInfo.BackgroundImg" class="pptbg"  style="width:100%"/>
+    <div class="flex-column cover" style="width:100%;height:100%;" v-if="pageInfo">
+        <img :src="pageInfo.BackgroundImg" class="pptbg"  style="width:100%;height:100%;object-fit: fill !important;"/>
         <div
         :style="`width:62%; font-size:16px; text-align:center; line-height:1.6; color:${$parent.pptCoverTextColor||'#fff'}; position:absolute; right:20px; top:50%;zIndex:20;`">
         <p :style="`height:5px;marginBottom:21px;`"></p>

+ 1 - 1
src/views/ppt_manage/newVersion/components/catalog/pptContent.vue

@@ -320,7 +320,7 @@ export default {
       position:relative;
       width:100%;
       background: url('~@/assets/img/pptnextimg.png') no-repeat center;
-      background-size: cover;
+      background-size: 100%;
       margin-bottom: 20px;
       border: 4px solid transparent;
       .ppt-info{

+ 2 - 2
src/views/ppt_manage/newVersion/pptEditor.vue

@@ -2,7 +2,7 @@
   <div class="page-wrap">
     <div class="index-wrap ppt-page-wrap flex-column">
         <div class="cover-wrap" @click="openChooseCover">
-            <div class="cover" :style="'background: no-repeat center/cover url('+firstPage.BackgroundImg+')'">
+            <div class="cover" :style="'background: no-repeat center/cover url('+firstPage.BackgroundImg+');background-color:#F2F6FA;'">
                 <img src="~@/assets/img/ppt_m/add_first.png" />
             </div>
             <p class="hint-text">选择封面页</p>
@@ -313,7 +313,7 @@ export default {
         firstPage:{
             Title:'',
             ReportType:'',
-            BackgroundImg:'https://hzstatic.hzinsights.com/ppt/bg3.jpg',
+            BackgroundImg:'',
             PptDate:(new Date().getFullYear())+'年'+(new Date().getMonth()+1)+'月',
             BackIndex:0,
             TemplateType:1

+ 2 - 2
src/views/ppt_manage/newVersion/pptEnEditor.vue

@@ -2,7 +2,7 @@
   <div class="page-wrap">
     <div class="index-wrap ppt-page-wrap flex-column">
         <div class="cover-wrap" @click="openChooseCover">
-            <div class="cover" :style="'background: no-repeat center/cover url('+firstPage.BackgroundImg+')'">
+            <div class="cover" :style="'background: no-repeat center/cover url('+firstPage.BackgroundImg+');background-color:#F2F6FA;'">
                 <img src="~@/assets/img/ppt_m/add_first.png" />
             </div>
             <p class="hint-text">选择封面页</p>
@@ -336,7 +336,7 @@ export default {
         firstPage:{
             Title:'',
             ReportType:'',
-            BackgroundImg:'https://hzstatic.hzinsights.com/ppt/bg3.jpg',
+            BackgroundImg:'',
             PptDate:(new Date().getFullYear())+'.'+(new Date().getMonth()+1),
             BackIndex:0,
             TemplateType:1

+ 6 - 4
src/views/ppt_manage/newVersion/pptEnPresent.vue

@@ -39,11 +39,11 @@
           <!-- <div class="image-move" v-if="dragShow" @mousedown.stop="handleMoveStart" ></div> -->
           <div class="ppt-wrap" @wheel.stop="normalMouseWheel">
             <!-- 封面 -->
-            <div class="ppt-item" id="cover" v-if="currentIndex===0">
+            <div class="ppt-item" id="cover" v-if="currentIndex===0" style="background-size:20%;">
               <Cover :pageInfo="coverInfo.page" v-show="coverInfo.page"></Cover>
             </div>
             <!-- 封底 -->
-            <div class="ppt-item" id="back" v-else-if="currentIndex===pageList.length+1">
+            <div class="ppt-item" id="back" v-else-if="currentIndex===pageList.length+1" style="background-size:20%;">
               <img :src="pptBackImage" class="pptbg" style="width:100%;height:100%;object-fit: fill !important;"/>
             </div>
             <!-- PPT内容 -->
@@ -97,11 +97,11 @@
         @mousedown.stop="(e)=>{handleMouse('down',e)}" @mouseup="(e)=>{handleMouse('up',e)}" @wheel.stop="normalMouseWheel" @contextmenu="handleContextMenu">
           <div class="ppt-wrap">
             <!-- 封面 -->
-            <div class="ppt-item" id="cover" v-if="currentIndex===0">
+            <div class="ppt-item" id="cover" v-if="currentIndex===0" style="background-size:20%;">
               <Cover :pageInfo="coverInfo.page" v-show="coverInfo.page"></Cover>
             </div>
             <!-- 封底 -->
-            <div class="ppt-item" id="back" v-else-if="currentIndex===pageList.length+1">
+            <div class="ppt-item" id="back" v-else-if="currentIndex===pageList.length+1" style="background-size:20%;">
               <img :src="pptBackImage" class="pptbg" style="width:100%;height:100%;object-fit: fill !important;"/>
             </div>
             <!-- PPT内容 -->
@@ -302,6 +302,7 @@ export default {
         this.sheetListHandle(sheetElements);
       }
       this.currentKey = 1
+      this.pptBgImage&&$('.ppt-item').css('background-image',`url(${this.pptBgImage})`);
       this.dataLoading.close();
     },
     //根据id获取ppt数据
@@ -559,6 +560,7 @@ export default {
           $('.fullscreen .ppt-wrap').css('width','1100px')
           $('.ppt-item').css('transform',`scale(1)`)
           this.changeCurrentItem(this.currentItem)
+          $('.ppt-item').css('background-image',`url(${this.pptBgImage})`);
         })
       }else{
         this.$nextTick(()=>{

+ 4 - 4
src/views/ppt_manage/newVersion/pptEnPublish.vue

@@ -19,7 +19,7 @@
       <template v-if="loadingAll">
         <div class="ppt-wrap flex-center">
           <!-- 封面 -->
-          <div class="ppt-item" id="cover">
+          <div class="ppt-item" id="cover" style="background-size:20%;">
             <Cover :pageInfo="coverInfo.page"></Cover>
           </div>
           <!-- 内容 -->
@@ -37,7 +37,7 @@
             </component>
           </div>
           <!-- 封底 -->
-          <div class="ppt-item" id="back" v-if="pptBackImage.length">
+          <div class="ppt-item" id="back" v-if="pptBackImage.length" style="background-size:20%;">
             <img :src="pptBackImage" class="pptbg" style="width:100%;height:100%;object-fit: fill !important;"/>
           </div>
         </div>
@@ -394,14 +394,14 @@ export default {
       })
       //生成的ppt需要可以在封面页更改标题和类型,所以封面信息手动写入
       const coverInfo = [
-        {text:'—————————————————————————————————\n',options:{fontSize:16*0.75,breakLine:true}},
+        //{text:'—————————————————————————————————\n',options:{fontSize:16*0.75,breakLine:true}},
         {text:this.coverInfo.page.Title,options:{fontSize:28*0.75,breakLine:true}},
         {text:`\n${this.pptCoverCompenyName||'ETA'}`,
          options:{fontSize:16*0.75,breakLine:true}},
         {text:`\n — ${this.coverInfo.page.ReportType} —`,options:{fontSize:16*0.75,breakLine:true}}, 
         {text:`\n${this.pptCoverDepartName||'Research Department'}`,options:{fontSize:16*0.75,breakLine:true}},
         {text:this.coverInfo.page.PptDate,options:{fontSize:16*0.75,breakLine:true}},
-        {text:'\n—————————————————————————',options:{fontSize:16*0.75,breakLine:true}}
+        //{text:'\n—————————————————————————',options:{fontSize:16*0.75,breakLine:true}}
       ]
       cover.addText(coverInfo,{
         x:'38%',

+ 6 - 4
src/views/ppt_manage/newVersion/pptPresent.vue

@@ -39,11 +39,11 @@
           <!-- <div class="image-move" v-if="dragShow" @mousedown.stop="handleMoveStart" ></div> -->
           <div class="ppt-wrap" @wheel.stop="normalMouseWheel">
             <!-- 封面 -->
-            <div class="ppt-item" id="cover" v-if="currentIndex===0">
+            <div class="ppt-item" id="cover" v-if="currentIndex===0" style="background-size:20%;">
               <Cover :pageInfo="coverInfo.page" v-show="coverInfo.page"></Cover>
             </div>
             <!-- 封底 -->
-            <div class="ppt-item" id="back" v-else-if="currentIndex===pageList.length+1">
+            <div class="ppt-item" id="back" v-else-if="currentIndex===pageList.length+1" style="background-size:20%;">
               <img :src="pptBackImage" class="pptbg" style="width:100%;height:100%;object-fit: fill !important;"/>
             </div>
             <!-- PPT内容 -->
@@ -97,11 +97,11 @@
         @mousedown.stop="(e)=>{handleMouse('down',e)}" @mouseup="(e)=>{handleMouse('up',e)}" @wheel.stop="normalMouseWheel" @contextmenu="handleContextMenu" >
           <div class="ppt-wrap">
             <!-- 封面 -->
-            <div class="ppt-item" id="cover" v-if="currentIndex===0">
+            <div class="ppt-item" id="cover" v-if="currentIndex===0" style="background-size:20%;">
               <Cover :pageInfo="coverInfo.page" v-show="coverInfo.page"></Cover>
             </div>
             <!-- 封底 -->
-            <div class="ppt-item" id="back" v-else-if="currentIndex===pageList.length+1">
+            <div class="ppt-item" id="back" v-else-if="currentIndex===pageList.length+1" style="background-size:20%;">
               <img :src="pptBackImage" class="pptbg" style="width:100%;height:100%;object-fit: fill !important;"/>
             </div>
             <!-- PPT内容 -->
@@ -300,6 +300,7 @@ export default {
         this.sheetListHandle(sheetElements);
       }
       this.currentKey = 1
+      this.pptBgImage&&$('.ppt-item').css('background-image',`url(${this.pptBgImage})`);
       this.dataLoading.close();
     },
     //根据id获取ppt数据
@@ -557,6 +558,7 @@ export default {
           $('.fullscreen .ppt-wrap').css('width','1100px')
           $('.ppt-item').css('transform',`scale(1)`)
           this.changeCurrentItem(this.currentItem)
+          $('.ppt-item').css('background-image',`url(${this.pptBgImage})`);
         })
       }else{
         this.$nextTick(()=>{

+ 4 - 4
src/views/ppt_manage/newVersion/pptPublish.vue

@@ -20,7 +20,7 @@
       <template v-if="loadingAll">
         <div class="ppt-wrap flex-center">
           <!-- 封面 -->
-          <div class="ppt-item" id="cover">
+          <div class="ppt-item" id="cover" style="background-size:20%;">
             <Cover :pageInfo="coverInfo.page"></Cover>
           </div>
           <!-- 内容 -->
@@ -38,7 +38,7 @@
             </component>
           </div>
           <!-- 封底 -->
-          <div class="ppt-item" id="back" v-if="pptBackImage.length">
+          <div class="ppt-item" id="back" v-if="pptBackImage.length" style="background-size:20%;">
             <img :src="pptBackImage" class="pptbg" style="width:100%;height:100%;object-fit: fill !important;"/>
           </div>
         </div>
@@ -474,13 +474,13 @@ export default {
       })
       //生成的ppt需要可以在封面页更改标题和类型,所以封面信息手动写入
       const coverInfo = [
-        {text:'—————————————————————————————————\n',options:{fontSize:16*0.75,breakLine:true}},
+        //{text:'—————————————————————————————————\n',options:{fontSize:16*0.75,breakLine:true}},
         {text:this.coverInfo.page.Title,options:{fontSize:28*0.75,breakLine:true}},
         {text:`\n— ${this.pptCoverCompenyName||'ETA'} ● ${this.coverInfo.page.ReportType} —`,
          options:{fontSize:16*0.75,breakLine:false}},
         {text:`\n${this.pptCoverDepartName||'投研部'}`,options:{fontSize:16*0.75,breakLine:true}},
         {text:this.coverInfo.page.PptDate,options:{fontSize:16*0.75,breakLine:true}},
-        {text:'\n—————————————————————————',options:{fontSize:16*0.75,breakLine:true}}
+        //{text:'\n—————————————————————————',options:{fontSize:16*0.75,breakLine:true}}
       ]
       cover.addText(coverInfo,{
         x:'38%',

+ 17 - 2
src/views/predictEdb_manage/predictEdb.vue

@@ -1093,8 +1093,23 @@ export default {
 				} else {
 					//计算指标
 					this.setComputedDialogForm(res.Data,type);
-					this.computed_type =[47,48].includes(Source)?'joint': Source;
-					this.computed_type = [72,73].includes(Source)? 'alpha': Source;
+
+					switch (Source) {
+						case 47: 
+						case 48: 
+							this.computed_type = 'joint';
+							break
+						case 72:
+						case 73:
+							this.computed_type = 'alpha';
+							break
+						default:
+							this.computed_type = Source;
+							break
+					}
+					
+					// this.computed_type =[47,48].includes(Source)?'joint': Source;
+					// this.computed_type = [72,73].includes(Source)? 'alpha': Source;
 				}
 
 			})

+ 2 - 1
src/views/sandbox_manage/index.vue

@@ -404,7 +404,8 @@ export default {
     },
 
     editChildSand(item) {
-      ({SandboxId,VersionCode,type} = item)
+      // ({SandboxId,VersionCode,type} = item)
+      let {SandboxId,VersionCode,type} = item
        // 编辑前校验
       sandInterface
         .mark({

+ 312 - 0
src/views/smartReport/components/BaseInfo.vue

@@ -0,0 +1,312 @@
+<template>
+    <el-dialog
+        title="基础信息"
+        :visible.sync="show"
+        :modal-append-to-body="false"
+        :close-on-click-modal="false"
+        :center="true"
+        v-dialogDrag
+        custom-class="dialogclass"
+        width="440px"
+        @close="handleClose"
+    >
+        <el-form 
+            :model="formData" 
+            :rules="rules" 
+            ref="baseinfoForm"
+            class="baseinfo-form-wrap"
+        >  
+            <el-form-item prop="type">
+                <el-radio-group v-model="formData.type" :disabled="id" @change="handleUpdateBaseInfo">
+                    <el-radio :label="1">新增报告</el-radio>
+                    <el-radio :label="2">继承报告</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item prop="classify">
+                <el-cascader
+					ref="cascader"
+					:options="classifyArr"
+					v-model="formData.classify"
+					placeholder="请选择分类"
+					size="medium"
+                    style="width:340px"
+                    @change="handleUpdateBaseInfo"
+				/>
+            </el-form-item>
+            <el-form-item prop="title">
+                <el-input placeholder="请输入标题" v-model="formData.title" style="width:340px"></el-input>
+            </el-form-item>
+            <el-form-item prop="abstract">
+                <el-input type="textarea" placeholder="请输入摘要" v-model="formData.abstract" style="width:340px"></el-input>
+            </el-form-item>
+            <el-form-item prop="author">
+				<el-select
+					v-model="formData.author"
+					multiple
+					placeholder="请选择作者"
+					size="medium"
+					style="width: 340px"
+				>
+					<el-option
+						v-for="(item, i) in authorlist"
+						:key="i"
+						:label="item.ReportAuthor"
+						:value="item.ReportAuthor"
+					></el-option>
+				</el-select>
+			</el-form-item>
+            <el-form-item prop="frequency">
+                <el-select
+					v-model="formData.frequency"
+					placeholder="请选择频度"
+					size="medium"
+					style="width: 340px"
+				>
+                    <el-option label="年度" value="年度"></el-option>
+                    <el-option label="半年度" value="半年度"></el-option>
+                    <el-option label="季度" value="季度"></el-option>
+                    <el-option label="月度" value="月度"></el-option>
+                    <el-option label="双周度" value="双周度"></el-option>
+                    <el-option label="周度" value="周度"></el-option>
+                    <el-option label="日度" value="日度"></el-option>
+                    <el-option label="不定时" value="不定时"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item prop="time">
+                <el-date-picker
+					v-model="formData.time"
+					type="date"
+					value-format="yyyy-MM-dd"
+					placeholder="选择日期"
+					size="medium"
+					:clearable="false"
+					style="width: 340px"
+				></el-date-picker>
+            </el-form-item>
+        </el-form>
+        <div style="text-align:center;margin-top:60px;margin-bottom:40px">
+            <el-button type="primary" plain style="width:120px" @click="handleClose">取消</el-button>
+            <el-button type="primary" style="width:120px" @click="handleConfirm">确定</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+import {apiSmartReport}  from '@/api/modules/smartReport'
+export default {
+    name:"BaseInfo",
+    model:{
+        prop:'show',
+        event:'showChange'
+    },
+    props:{
+        show:{
+            type:Boolean,
+            default:false
+        },
+        id:{
+            type:Number,
+            default:0
+        }
+    },
+    watch: {
+        show(n){
+            if(!n){
+                this.formData.type=1
+                this.formData.classify=[]
+                this.formData.title=''
+                this.formData.abstract=''
+                this.formData.author=['FICC团队']
+                this.formData.frequency='日度'
+                this.formData.time=this.$moment().format('YYYY-MM-DD')
+            }else{
+                if(this.id){
+                    apiSmartReport.reportDetail({
+                        SmartReportId:Number(this.id)
+                    }).then(res=>{
+                        if(res.Ret===200){
+                            this.formData.type=res.Data.AddType
+                            this.formData.classify=[res.Data.ClassifyIdFirst,res.Data.ClassifyIdSecond]
+                            this.formData.title=res.Data.Title
+                            this.formData.abstract=res.Data.Abstract
+                            this.formData.author=res.Data.Author ? res.Data.Author.split(',') : []
+                            this.formData.frequency=res.Data.Frequency
+                            this.formData.time=res.Data.CreateTime
+                        }
+                    })
+                }
+            }
+        }  
+    },
+    data() {
+        return {
+            rules:{
+                type:[{ required: true, message: '请选择报告类型', trigger: 'change' }],
+                classify:[{ required: true, message: '请选择报告分类', trigger: 'change' }],
+                title:[{ required: true, message: '请填写报告标题', trigger: 'blur' }],
+            },
+            formData:{
+                type:1,
+                classify:[],
+                title:'',
+                abstract:'',
+                author:['FICC团队'],
+                frequency:'日度',
+                time:this.$moment().format('YYYY-MM-DD')||''
+            },
+            classifyArr:[],
+            authorlist:[]
+        }
+    },
+   
+    methods: {
+        handleClose(){
+            this.$emit('showChange', false)
+        },
+
+        handleConfirm(){
+            this.$refs.baseinfoForm.validate((valid)=>{
+                if(valid){
+
+                    const params={
+                        AddType: this.formData.type,
+                        ClassifyIdFirst: this.formData.classify[0]?this.formData.classify[0]:0,
+                        ClassifyNameFirst: '',
+                        ClassifyIdSecond:this.formData.classify[1]?this.formData.classify[1]:0,
+                        ClassifyNameSecond:'',
+                        Title: this.formData.title,
+                        Abstract: this.formData.abstract,
+                        Author:
+                            this.formData.author.length > 0
+                                ? this.formData.author.join(',')
+                                : '',
+                        Frequency: this.formData.frequency,
+                        CreateTime: this.formData.time,
+                    }
+                    this.classifyArr.forEach(item=>{
+                        if(item.value===params.ClassifyIdFirst){
+                            params.ClassifyNameFirst=item.label
+                            const arr=item.children||[]
+                            arr.forEach(_item=>{
+                                if(_item.value===params.ClassifyIdSecond){
+                                    params.ClassifyNameSecond=_item.label
+                                }
+                            })
+                        }
+                    })
+                    // 编辑
+                    if(this.id){
+                        this.$emit('save',params)
+                        return
+                    }
+                    apiSmartReport.reportAdd(params).then(res=>{
+                        if(res.Ret===200){
+                            this.handleClose()
+                            let { href } = this.$router.resolve({
+                                path: "/smartReportEdit",
+                                query: { id: res.Data.SmartReportId },
+                            });
+                            window.open(href, "_blank");
+                        }
+                    })
+
+                }
+            })
+        },
+
+        handleUpdateBaseInfo(){
+            if(this.formData.type===1){
+                if(this.formData.classify.length===2){
+                    this.formData.title=this.getSelectClassifyName()[1]
+                }
+                return
+            }
+            //获取上次报告
+            apiSmartReport.getLastReport({
+                ClassifyIdFirst:this.formData.classify[0],
+                ClassifyIdSecond:this.formData.classify[1]
+            }).then(res=>{
+                if(res.Ret!==200) return
+                if (res.Data == null) {
+					this.$message.warning('此分类暂无报告');
+					return false;
+				}
+                this.formData.title=res.Data.Title
+                this.formData.abstract=res.Data.Abstract
+                this.formData.author=res.Data.Author ? res.Data.Author.split(',') : ''
+                this.formData.frequency=res.Data.Frequency
+            })
+        },
+
+        // 获取选择的分类名称
+        getSelectClassifyName(){
+            let arr=[]
+            this.classifyArr.forEach(item=>{
+                if(this.formData.classify[0]&&item.value===this.formData.classify[0]){
+                    arr.push(item.label)
+                    if(item.children&&item.children.length>0){
+                        item.children.forEach(_item=>{
+                            if(this.formData.classify[1]&&_item.value===this.formData.classify[1]){
+                                arr.push(_item.label)
+                            }
+                        })
+                    }
+                }
+            })
+            return arr
+        },
+
+        // 获取分类
+        getclassifylist() {
+            let params = { CurrentIndex: 0, PageSize: 1000, KeyWord: "",HideDayWeek:1,/*不显示晨报/周报*/ };
+            apiSmartReport.classifyList(params).then((res) => {
+                if (res.Ret == 200 && Array.isArray(res.Data.List)) {
+                    this.classifyArr = [];
+                    res.Data.List.forEach((item, index) => {
+                        let newitem = {
+                            label: item.ClassifyName,
+                            value: item.Id,
+                        };
+                        if (item.Child) {
+                            let childnode = [];
+                            item.Child.forEach((itemchild, i) => {
+                                childnode.push({
+                                    label: itemchild.ClassifyName,
+                                    value: itemchild.Id,
+                                });
+                            });
+                            newitem.children = childnode;
+                        }
+                        this.classifyArr.push(newitem);
+                    });
+                }
+            });
+        },
+        // 获取作者
+        getreportauthor() {
+			apiSmartReport.reportAuthor({}).then((res) => {
+				if (res.Ret == 200) {
+					this.authorlist = res.Data.List || [];
+				}
+			});
+		},
+    },
+    mounted(){
+        this.getclassifylist()
+        this.getreportauthor()
+    },
+}
+</script>
+
+<style lang="scss">
+.baseinfo-form-wrap{
+    .el-input{
+        width: 100%;
+    }
+    .el-form-item{
+        width: 340px;
+        margin-left: auto;
+        margin-right: auto;
+    }
+}
+</style>

+ 20 - 0
src/views/smartReport/components/ChartComp.vue

@@ -0,0 +1,20 @@
+<template>
+    <div 
+        class="report-comp-item chart-comp"
+        style="min-height:300px;width:100%;height:100%;overflow: hidden;display:flex;flex-direction: column;margin-bottom:10px"
+    >
+        <iframe :src="compData.content" style="flex:1;width:100%;height:100%;border-width:0px;"></iframe>
+    </div>
+</template>
+
+<script>
+export default {
+    props:{
+        compData:{}
+    }
+}
+</script>
+
+<style>
+
+</style>

+ 106 - 0
src/views/smartReport/components/ETAChart.vue

@@ -0,0 +1,106 @@
+<template>
+    <div class="eta-chart-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span :class="['item',activeType==='ETA图库'?'active':'']" @click="activeTypeChange('ETA图库')">ETA图库</span>
+                <span :class="['item',activeType==='MyETA'?'active':'']" @click="activeTypeChange('MyETA')">MyETA</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="图表名称"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    v-if="activeType==='ETA图库'"
+                    @input="handleETAChartSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <ImportETAChart ref="ETAChartWrap" v-if="activeType==='ETA图库'"/>
+            <ImportMyETAChart @handleImportMyChart="handleImportMyChart" v-if="activeType==='MyETA'"/>
+        </div>
+    </div>
+</template>
+
+<script>
+import ImportETAChart from './ImportETAChart.vue';
+import ImportMyETAChart from './ImportMyETAChart.vue';
+export default {
+    name:"ETAChartWrap",
+    components:{ImportETAChart,ImportMyETAChart},
+    data() {
+        return {
+            keyword:'',
+            activeType:'ETA图库',
+        }
+    },
+    methods: {
+        activeTypeChange(e){
+            if(this.activeType===e) return
+            this.activeType=e
+        },
+
+        handleETAChartSearch(){
+            this.$refs.ETAChartWrap.handleSearch(this.keyword)
+        },
+
+        handleImportMyChart(list){
+            this.$emit('handleImportMyChart',list)
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.eta-chart-wrap{
+    width: 100%;
+    min-width: 600px;
+    overflow-x: auto;
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .left-card{
+            border-radius: 4px;
+            border: 1px solid var(--unnamed, #DCDFE6);
+            background: var(--gary-gy-3-disabled, #EBEFF6);
+            display: flex;
+            align-items: center;
+            padding: 6px;
+            .item{
+                cursor: pointer;
+                display: block;
+                width: 110px;
+                height: 30px;
+                line-height: 30px;
+                text-align: center;
+                border-radius: 4px;
+                font-size: 18px;
+                color: #666;
+                &.active{
+                    background: #FFF;
+                    color: #002D78;
+                }
+            }
+        }
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 0 20px 20px 20px;
+    }
+}
+</style>

+ 177 - 0
src/views/smartReport/components/ETAPriceChart.vue

@@ -0,0 +1,177 @@
+<template>
+    <div class="statistic-analysis-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span>商品价格曲线</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="图表名称"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    @input="handleSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <el-checkbox
+                class="onlyshowme-box"
+                v-model="isShowMe" 
+                @change="handleIsShowMeChange"
+            >只看我的</el-checkbox>
+            <div class="list-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+                <draggable
+                    :list="list"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="chart-list-box"
+                    animation="300"
+                    :sort="false"
+                    tag="div"
+                >
+                    <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.UniqueCode">
+                        <div class="title">{{item.ChartName}}</div>
+                        <div class="img" :style="'backgroundImage:url('+item.ChartImage+')'"></div>
+                    </div>
+                </draggable>
+                <tableNoData text="暂无图表" size="mini" v-if="list.length===0&&finished"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import futuresInterface from "@/api/modules/futuresBaseApi";
+export default {
+    data() {
+        return {
+            
+            keyword:'',
+            isShowMe:false,
+            list:[],
+            page:1,
+            pageSize:20,
+            finished:false
+        }
+    },
+    created(){
+        this.getChartList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const LINK_CHART_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/chartshow';
+            const obj={
+                compId:3,
+                compType:'chart',
+                content:`${LINK_CHART_URL}?code=${item.UniqueCode}`
+            }
+            return JSON.stringify(obj)
+        },
+
+        handleIsShowMeChange(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getChartList()
+        },
+
+        handleSearch(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getChartList()
+        },
+
+        /* 搜索图表分页 */
+        async getChartList(word) {
+            let params = {
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+                IsShowMe: this.isShowMe,
+            };
+            let res = await futuresInterface.searchChart(params);
+            if (res.Ret !== 200) return;
+            const arr = res.Data.List || [];
+            this.list =
+                this.page === 1
+                ? arr
+                : [...this.list, ...arr];
+            this.finished =  res.Data.Paging.IsEnd;
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getChartList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.statistic-analysis-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        .list-wrap{
+            flex: 1;
+            overflow-y: auto;
+
+        }
+    }
+}
+.chart-list-box{
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+    .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: cover;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+}
+</style>

+ 169 - 0
src/views/smartReport/components/ETASandBox.vue

@@ -0,0 +1,169 @@
+<template>
+    <div class="statistic-analysis-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span>沙盘图</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="沙盘名称/品种"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    @input="handleSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <div class="list-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+                <draggable
+                    :list="list"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="chart-list-box"
+                    animation="300"
+                    :sort="false"
+                    tag="div"
+                >
+                    <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.SandboxId">
+                        <div class="title">{{item.Name}}</div>
+                        <div class="img" :style="'backgroundImage:url('+item.PicUrl+')'"></div>
+                    </div>
+                </draggable>
+                <tableNoData text="暂无数据" size="mini" v-if="list.length===0&&finished"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import {sandInterface} from "@/api/api.js";
+export default {
+    data() {
+        return {
+            
+            keyword:'',
+            list:[],
+            page:1,
+            pageSize:20,
+            finished:false
+        }
+    },
+    created(){
+        this.getSandBoxList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const obj={
+                compId:2,
+                compType:'img',
+                content:item.PicUrl
+            }
+            return JSON.stringify(obj)
+        },
+
+        handleIsShowMeChange(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getSandBoxList()
+        },
+
+        handleSearch(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getSandBoxList()
+        },
+
+        /* 搜索图表分页 */
+        async getSandBoxList(word) {
+            let params = {
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+            };
+            let res = await sandInterface.sandlistByQuote(params);
+            if (res.Ret !== 200) return;
+            const arr = res.Data.List || [];
+            this.list =
+                this.page === 1
+                ? arr
+                : [...this.list, ...arr];
+            this.finished =  res.Data.Paging.IsEnd;
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getSandBoxList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.statistic-analysis-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        .list-wrap{
+            flex: 1;
+            overflow-y: auto;
+
+        }
+    }
+}
+.chart-list-box{
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+    .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: contain;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+}
+</style>

+ 130 - 0
src/views/smartReport/components/ETASheet.vue

@@ -0,0 +1,130 @@
+<template>
+    <div class="eta-sheet-wrap">
+        <div class="top-box">
+            <span>ETA表格</span>
+            <el-input
+                class="search-box"
+				placeholder="表格名称"
+				v-model="keyword"
+				size="medium"
+				prefix-icon="el-icon-search"
+                @input="handleSearch"
+			/>
+        </div>
+        <div class="main-box">
+            <draggable
+                :list="list"
+                :group="{ name: 'component', pull: 'clone', put: false }"
+                class="sheet-list-box"
+                animation="300"
+                :sort="false"
+                tag="div"
+            >
+                <div class="sheet-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.ExcelInfoId">
+                    <div class="title">{{item.ExcelName}}</div>
+                    <div class="img" :style="'backgroundImage:url('+item.ExcelImage+')'"></div>
+                </div>
+            </draggable>
+            <tableNoData text="暂无数据" size="mini" v-if="list.length===0"/>
+        </div>
+    </div>
+</template>
+
+<script>
+import * as sheetInterface from "@/api/modules/sheetApi.js";
+export default {
+    data() {
+        return {
+            keyword:'',
+            list:[]
+        }
+    },
+    created(){
+        this.getSheetList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const LINK_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/sheetshow';
+            // console.log(LINK_URL);
+            const obj={
+                compId:4,
+                compType:'sheet',
+                content:`${LINK_URL}?code=${item.UniqueCode}`
+            }
+            return JSON.stringify(obj)
+        },
+
+        handleSearch(){
+            this.getSheetList()
+        },
+        getSheetList() {
+            this.list=[]
+            sheetInterface.sheetList({
+                Keyword: this.keyword,
+                CurrentIndex: 1,
+                PageSize: 10000,
+            }).then((res) => {
+                if (res.Ret !== 200) return;
+                this.list = res.Data.List || [];
+            });
+        },
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.eta-sheet-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        .search-box{
+            width: 330px;
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        overflow-y: auto;
+        .sheet-list-box{
+            display: flex;
+            flex-wrap: wrap;
+            gap: 20px;
+            .sheet-item{
+                cursor: move;
+                padding: 0 10px 10px 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: contain;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+        }
+    }
+}
+</style>

+ 16 - 0
src/views/smartReport/components/ImgComp.vue

@@ -0,0 +1,16 @@
+<template>
+    <div 
+        class="report-comp-item img-comp" 
+        style="width:100%;height:100%;overflow-y: auto;margin-bottom:10px"
+    >
+        <img style="width:100%" :src="compData.content" alt="">
+    </div>
+</template>
+
+<script>
+export default {
+    props:{
+        compData:{}
+    }
+}
+</script>

+ 141 - 0
src/views/smartReport/components/ImgEdit.vue

@@ -0,0 +1,141 @@
+<template>
+    <div class="img-edit-wrap" @paste="handleListenPaste">
+        <div style="font-size:16px;margin-bottom:20px">图片上传</div>
+        <div class="main-box">
+            <input type="file" size="small" name="file" @change="fileSelected" id="file" class="true-file" style="display:none;">
+            <div class="upload-box" @click="clickinput">
+                <img class="bg" :src="content" alt="" v-if="content">
+                <img src="~@/assets/img/smartReport/icon15.png" alt="">
+                <div>点击上传图片</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import { bannerupload } from 'api/api.js';
+export default {
+    name:'ImgEdit',
+    props:{
+        content:''
+    },
+    created() {
+        window.addEventListener('paste',this.handleListenPaste)
+    },
+     
+    destroyed(){
+        window.removeEventListener('paste',this.handleListenPaste)
+    },
+    methods: {
+        clickinput(){  //上传模拟点击
+			$(`#file`).click();
+		},
+        fileSelected(file){  //选择文件上传
+            let hostfile=document.getElementById('file').files[0]||file
+			const that = this;
+		    if( hostfile ){
+		        let size = Math.floor(hostfile.size / 1024 / 1024);
+		        if( size>200 ){
+		            that.$message.warning('上传文件大小不能大于200M!');
+		            hostfile = {};
+		            return false
+		        }
+		        if( hostfile.name.toLowerCase().includes('.png') || hostfile.name.toLowerCase().includes('.jpg') || hostfile.name.toLowerCase().includes('.jpeg') ){
+		        	let form = new FormData();
+		        	form.append('file',hostfile);  //hostfile.name
+					bannerupload(form).then((res) => {
+						if( res.Ret === 200 ){
+							that.$emit('imgChange', res.Data.ResourceUrl)
+                            that.content=res.Data.ResourceUrl
+						}
+						$("#file").val('');
+						hostfile = {};
+					});
+				}else{
+					that.$message.warning('上传文件格式不正确!');
+		        }
+		    } 
+		},
+
+        handleListenPaste:_.throttle(async function(e){
+            if(!e.clipboardData) return
+            const clipboardDataItems = e.clipboardData.items
+            const clipboardDataFirstItem = clipboardDataItems[0]
+            if(clipboardDataFirstItem){
+                for (const item of clipboardDataItems) {
+                    if (item.kind === 'file' && item.type.indexOf('image') !== -1) {
+                        const imageFile = item.getAsFile()
+                        console.log('读取成功1',imageFile);
+                        if (imageFile) this.fileSelected(imageFile)
+                        return
+                    }
+                }
+                return
+            }
+            //clipboardData中没有图片,从navigator.clipboard.read里获取图片
+            let clipboardItems = null
+            try{
+                clipboardItems = await navigator.clipboard.read()
+            }catch(error){
+                this.$message.warning("剪贴板读取不到文件!")
+                return
+            }
+            const blob = await this.checkClipboardItems(clipboardItems)
+            console.log('读取成功2',blob);
+            if(blob) this.fileSelected(blob)
+
+        },1000),
+
+        checkClipboardItems(clipboard){
+            for(const Item of clipboard){
+                for(const type of Item.types){
+                    if(type.includes('image')){
+                        return Item.getType(type)
+                    }
+                }
+            }
+            return null
+        }
+    },
+
+
+
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+
+.main-box{
+    width: 100%;
+    height: calc(100vh - 150px);
+    border-radius: 4px;
+    border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+    background: #FFF;
+    padding: 30px;
+    .upload-box{
+        cursor: pointer;
+        width: 120px;
+        height: 120px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        border: 1px dashed var(--gary-gy-5-line, #C8CDD9);
+        background: var(--gary-gy-1-bottom-bk, #F8F8F8);
+        color: #999;
+        position: relative;
+        .bg{
+            width: 100%;
+            height: 100%;
+            position: absolute;
+            left: 0;
+            top: 0;
+            z-index: 10;
+            background: #FFF;
+        }
+    }
+}
+</style>

+ 142 - 0
src/views/smartReport/components/ImportETAChart.vue

@@ -0,0 +1,142 @@
+<template>
+    <div class="import-eta-chart-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+            <el-checkbox
+                class="onlyshowme-box"
+                v-model="isShowMe" 
+                @change="handleIsShowMeChange"
+            >只看我的</el-checkbox>
+            <draggable
+                :list="list"
+                :group="{ name: 'component', pull: 'clone', put: false }"
+                class="chart-list-box"
+                animation="300"
+                :sort="false"
+                tag="div"
+            >
+                <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.UniqueCode">
+                    <div class="title">{{item.ChartName}}</div>
+                    <div class="img" :style="'backgroundImage:url('+item.ChartImage+')'"></div>
+                </div>
+            </draggable>
+            <tableNoData text="暂无图表" size="mini" v-if="list.length===0"/>
+    </div>
+</template>
+
+<script>
+import { dataBaseInterface } from "@/api/api.js";
+export default {
+    data() {
+        return {
+            isShowMe:false,
+            page:1,
+            pageSize:20,
+            list:[],
+            finished:false,
+            keyword:''
+        }
+    },
+    created() {
+        this.getETAChartList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const LINK_CHART_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/chartshow';
+            const obj={
+                compId:3,
+                compType:'chart',
+                content:`${LINK_CHART_URL}?code=${item.UniqueCode}`
+            }
+            return JSON.stringify(obj)
+        },
+
+        async getETAChartList(){
+            const res=await dataBaseInterface.chartSearchByEs({
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+                IsShowMe: this.isShowMe,
+            })
+            if(res.Ret===200){
+                const arr=res.Data.List || []
+                this.list=[...this.list,...arr]
+                this.finished=res.Data.Paging.IsEnd
+            }
+        },
+
+        handleIsShowMeChange(){
+            this.page=1
+            this.finished=false
+            this.list=[]
+            this.getETAChartList()
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getETAChartList()
+        },
+
+        handleSearch(key){
+            this.page=1
+            this.finished=false
+            this.list=[]
+            this.keyword=key
+            this.getETAChartList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.import-eta-chart-wrap{
+    height: 100%;
+    width: 100%;
+    position: relative;
+    overflow-y: auto;
+    overflow-x: hidden;
+    padding-top: 20px;
+    .onlyshowme-box{
+            display: block;
+            position: sticky;
+            top: -20px;
+            padding: 20px 0;
+            background-color: #FFF;
+        }
+        .chart-list-box{
+            display: flex;
+            flex-wrap: wrap;
+            gap: 20px;
+            .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: cover;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+        }
+}
+</style>

+ 170 - 0
src/views/smartReport/components/ImportMyETAChart.vue

@@ -0,0 +1,170 @@
+<template>
+    <div class="import-mychart-wrap">
+        <div class="select-box">
+			<el-select 
+				style="width:300px" 
+				v-model="selectMyChartClassify" 
+				filterable 
+				placeholder="图分类名称"
+				@change="handleChange"
+			>
+				<el-option
+					v-for="item in myChartClassifyList"
+					:key="item.MyChartClassifyId"
+					:label="item.MyChartClassifyName"
+					:value="item.MyChartClassifyId"
+				>
+				</el-option>
+			</el-select>
+			<el-button type="primary" @click="handleImport">一键导入</el-button>
+			<el-tooltip class="item" effect="dark" content="该操作将批量导入所选图分类下所有图表" placement="top-start">
+				<i style="font-size:24px;color: #666;" class="el-icon-question"></i>
+			</el-tooltip>
+		</div>
+		<div class="list" v-infinite-scroll="load" v-if="list.length>0">
+            <div class="chart-item" v-for="item in list" :key="item.UniqueCode" @click="handleClickItem">
+                <div class="title">{{item.ChartName}}</div>
+                <div class="img" :style="'backgroundImage:url('+item.ChartImage+')'"></div>
+            </div>
+		</div>
+        <tableNoData text="无数据" size="mini" v-if="list.length===0"/>
+    </div>
+</template>
+
+<script>
+import { mychartInterface } from '@/api/api.js';
+export default {
+    data() {
+        return {
+            myChartClassifyList:[],
+            selectMyChartClassify:'',
+            pageSize:10000,
+			CurrentIndex: 1,
+			list:[],
+            loading:false,
+            finished:false,
+
+        }
+    },
+    props:{
+        showEnMark:{
+            type:Boolean,
+            default:false
+        }
+    },
+    mounted(){
+        this.getMyChartClassify()
+    },  
+    methods: {
+        //获取我的图库中分类
+		getMyChartClassify(){
+			mychartInterface.classifyList().then(res=>{
+				if (res.Ret !== 200) return;
+				this.myChartClassifyList=res.Data?res.Data.List:[]
+			})
+		},
+        handleChange(){
+            this.CurrentIndex=1
+            this.list=[]
+            this.finished=false
+            this.loading=false
+            this.handleGetMyChartList()
+        },
+		async handleGetMyChartList(){
+            if(!this.selectMyChartClassify) return
+            this.loading=true
+            const res=await  mychartInterface.myList({
+				PageSize: this.pageSize,
+				CurrentIndex: this.CurrentIndex,
+				MyChartClassifyId: this.selectMyChartClassify || 0
+			})
+            this.loading=false
+            const arr=res.Data?res.Data.List.filter(_=>!_.Disabled):[]
+            this.list=[...this.list,...arr]
+            if(!res.Data){
+                this.finished=true
+            }
+            if(res.Data&&res.Data.Paging.IsEnd){
+                this.finished=true
+            }
+		},
+
+        load(){
+            if(this.finished) return
+            this.CurrentIndex++
+            this.handleGetMyChartList()
+        },
+
+        handleImport:_.throttle(function(){
+            if(!this.selectMyChartClassify){
+                this.$message.warning('请选择分类')
+                return
+            }
+            if(this.list.length==0){
+                this.$message.warning('该分类无图表')
+                return
+            }
+            this.$emit('handleImportMyChart',this.list)
+            setTimeout(() => {
+                this.CurrentIndex=1
+                this.list=[]
+                this.finished=false
+                this.loading=false
+                this.selectMyChartClassify=''
+            }, 300);
+        },1000),
+
+        handleClickItem(){
+            this.$message.warning('此处不支持单个插入图表')
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.import-mychart-wrap{
+    position: relative;
+    height: 100%;
+    padding-top: 20px;
+    .select-box{
+        position: absolute;
+        top: -75px;
+        right: 0;
+    }
+    .list{
+        max-height: calc(100vh - 220px);;
+        overflow-x: hidden;
+		overflow-y: auto;
+        display: flex;
+            flex-wrap: wrap;
+            gap: 20px;
+            .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                max-height: 260px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: cover;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+    }
+}
+</style>

+ 169 - 0
src/views/smartReport/components/SemanticAnalysis.vue

@@ -0,0 +1,169 @@
+<template>
+    <div class="statistic-analysis-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span>语义分析</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="关键词搜索"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    @input="handleSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <div class="list-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+                <draggable
+                    :list="list"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="chart-list-box"
+                    animation="300"
+                    :sort="false"
+                    tag="div"
+                >
+                    <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.SaCompareId">
+                        <div class="title">{{item.Title}}</div>
+                        <div class="img" :style="'backgroundImage:url('+item.ResultImg+')'"></div>
+                    </div>
+                </draggable>
+                <tableNoData text="暂无数据" size="mini" v-if="list.length===0&&finished"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import {semanticInterface} from '@/api/modules/semanticsApi.js';
+export default {
+    data() {
+        return {
+            
+            keyword:'',
+            list:[],
+            page:1,
+            pageSize:20,
+            finished:false
+        }
+    },
+    created(){
+        this.getSemanticList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const obj={
+                compId:2,
+                compType:'img',
+                content:item.ResultImg
+            }
+            return JSON.stringify(obj)
+        },
+
+        handleIsShowMeChange(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getSemanticList()
+        },
+
+        handleSearch(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getSemanticList()
+        },
+
+        /* 搜索图表分页 */
+        async getSemanticList(word) {
+            let params = {
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+            };
+            let res = await semanticInterface.compareSearch(params);
+            if (res.Ret !== 200) return;
+            const arr = res.Data.List || [];
+            this.list =
+                this.page === 1
+                ? arr
+                : [...this.list, ...arr];
+            this.finished =  res.Data.Paging.IsEnd;
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getSemanticList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.statistic-analysis-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        .list-wrap{
+            flex: 1;
+            overflow-y: auto;
+
+        }
+    }
+}
+.chart-list-box{
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+    .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: contain;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+}
+</style>

+ 41 - 0
src/views/smartReport/components/SheetComp.vue

@@ -0,0 +1,41 @@
+<template>
+    <div 
+        class="report-comp-item sheet-comp"
+        style="width:100%;overflow: hidden;margin-bottom:10px"
+    >
+        <iframe :class="id" :src="compData.content" width="100%" style="border-width:0px;"></iframe>
+    </div>
+</template>
+
+<script>
+function GetQueryString(url) {
+    let urlStr=url.split('?')[1]
+    let obj={}
+    let paramsArr=urlStr.split('&')
+    for (let index = 0; index < paramsArr.length; index++) {
+        let arr=paramsArr[index].split('=')
+        obj[arr[0]]=arr[1]
+    }
+    return obj
+}
+export default {
+    props:{
+        compData:{}
+    },
+    computed: {
+        id(){
+            if(this.compData.content){
+                // console.log(this.compData.content);
+                let params = GetQueryString(this.compData.content)
+                // console.log(params);
+                return `iframe${params.code}`
+            }
+            return ''
+        }
+    },
+}
+</script>
+
+<style>
+
+</style>

+ 243 - 0
src/views/smartReport/components/StatisticAnalysis.vue

@@ -0,0 +1,243 @@
+<template>
+    <div class="statistic-analysis-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span
+                    :class="['item',activeType===item.key?'active':'']" 
+                    @click="activeTypeChange(item.key)"
+                    v-for="item in typeOpts"
+                    :key="item.key"
+                >{{item.name}}</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="图表名称"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    @input="handleSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <el-checkbox
+                class="onlyshowme-box"
+                v-model="isShowMe" 
+                @change="handleIsShowMeChange"
+            >只看我的</el-checkbox>
+            <div class="list-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+                <draggable
+                    :list="list"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="chart-list-box"
+                    animation="300"
+                    :sort="false"
+                    tag="div"
+                >
+                    <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.UniqueCode">
+                        <div class="title">{{item.ChartName}}</div>
+                        <div class="img" :style="'backgroundImage:url('+item.ChartImage+')'"></div>
+                    </div>
+                </draggable>
+                <tableNoData text="暂无图表" size="mini" v-if="list.length===0&&finished"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import chartRelevanceApi from "@/api/modules/chartRelevanceApi";
+import {
+  fittingEquationInterface,
+  statisticFeatureInterface,
+} from "@/api/modules/chartRelevanceApi";
+export default {
+    data() {
+        return {
+            typeOpts:[
+                {
+                    name:'相关性',
+                    key:'相关性'
+                },
+                {
+                    name:'拟合方程曲线',
+                    key:'拟合方程曲线'
+                },
+                {
+                    name:'统计特征',
+                    key:'统计特征'
+                }
+            ],
+            keyword:'',
+            isShowMe:false,
+            activeType:'相关性',
+
+            list:[],
+            page:1,
+            pageSize:20,
+            finished:false
+        }
+    },
+    created(){
+        this.getChartList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const LINK_CHART_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/chartshow';
+            const obj={
+                compId:3,
+                compType:'chart',
+                content:`${LINK_CHART_URL}?code=${item.UniqueCode}`
+            }
+            return JSON.stringify(obj)
+        },
+
+        activeTypeChange(e){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.keyword=''
+            this.isShowMe=false
+            this.activeType=e
+            this.getChartList()
+        },
+
+        handleIsShowMeChange(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getChartList()
+        },
+
+        handleSearch(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getChartList()
+        },
+
+        /* 搜索图表分页 */
+        async getChartList(word) {
+            let params = {
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+                IsShowMe: this.isShowMe,
+            };
+            let res = null;
+            if (this.activeType === '相关性') {
+                res = await chartRelevanceApi.searchChart(params);
+            } else if (this.activeType === '拟合方程曲线') {
+                res = await fittingEquationInterface.searchChart(params);
+            } else if (this.activeType === '统计特征') {
+                res = await statisticFeatureInterface.searchChart(params);
+            }
+
+            if (res.Ret !== 200) return;
+            const arr = res.Data.List || [];
+            this.list =
+                this.page === 1
+                ? arr
+                : [...this.list, ...arr];
+            this.finished =  res.Data.Paging.IsEnd;
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getChartList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.statistic-analysis-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .left-card{
+            border-radius: 4px;
+            border: 1px solid var(--unnamed, #DCDFE6);
+            background: var(--gary-gy-3-disabled, #EBEFF6);
+            display: flex;
+            align-items: center;
+            gap: 0 3px;
+            padding: 6px;
+            .item{
+                cursor: pointer;
+                display: block;
+                width: 110px;
+                height: 30px;
+                line-height: 30px;
+                text-align: center;
+                border-radius: 4px;
+                font-size: 18px;
+                color: #666;
+                &.active{
+                    background: #FFF;
+                    color: #002D78;
+                }
+            }
+        }
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        .list-wrap{
+            flex: 1;
+            overflow-y: auto;
+
+        }
+    }
+}
+.chart-list-box{
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+    .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: cover;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+}
+</style>

+ 20 - 0
src/views/smartReport/components/TextComp.vue

@@ -0,0 +1,20 @@
+<template>
+    <div 
+        class="report-comp-item text-comp" 
+        style="width:100%;height: 100%;overflow-y: auto;margin-bottom:10px"
+    >
+        <div class="rich-text-box" v-html="compData.content"></div>
+    </div>
+</template>
+
+<script>
+export default {
+    props:{
+        compData:{}
+    }
+}
+</script>
+
+<style>
+
+</style>

+ 112 - 0
src/views/smartReport/components/TextEdit.vue

@@ -0,0 +1,112 @@
+<template>
+    <div class="text-edit-wrap">
+        <div style="font-size:16px;margin-bottom:20px">文本编辑</div>
+        <froala
+			id="froala-editor"
+			ref="froalaEditor"
+			:tag="'textarea'"
+			:config="froalaConfig"
+			v-model="html"
+		/>
+    </div>
+</template>
+
+<script>
+import VueFroala from 'vue-froala-wysiwyg';
+export default {
+    name:'TextEdit',
+    props:{
+        content:''
+    },
+    data() {
+        const that = this;
+        return {
+            html:this.content||'',
+            editor: null,
+            lastEditRange: null,
+            froalaConfig:{
+                toolbarButtons: [
+                    "textColor",
+                    "bold",
+                    "italic",
+                    "underline",
+                    "strikeThrough",
+                    "subscript",
+                    "superscript",
+                    "fontFamily",
+                    "fontSize",
+                    "color",
+                    "inlineClass",
+                    "inlineStyle",
+                    "paragraphStyle",
+                    "lineHeight",
+                    "paragraphFormat",
+                    "align",
+                    "formatOL",
+                    "formatUL",
+                    "outdent",
+                    "indent",
+                    "quote",
+                    "specialCharacters",
+                    "insertHR",
+                    "selectAll",
+                    "clearFormatting",
+                    "html",
+                    "undo",
+                    "redo",
+                ],
+                height: 800,
+                fontSize: ["12", "14", "16", "18", "20", "24", "28", "32", "36", "40"],
+                fontSizeDefaultSelection: "16",
+                theme: "dark", //主题
+                placeholderText: "请输入内容",
+                language: "zh_cn", //国际化
+                imageDefaultWidth: false,
+                quickInsertEnabled: false,
+                toolbarVisibleWithoutSelection: true, //是否开启 不选中模式
+                toolbarSticky: false, //操作栏是否自动吸顶
+                saveInterval: 0,
+                events: {
+                    //this.editor 定义在vue data 中
+                    initialized: function () {
+                        // this.editor = editor;
+                        that.editor = this;
+                        // that.editor.html.set(that.value);
+                        // that.setHtml()
+                    },
+                    keyup: function (e, editor) {
+                        //添加事件,在每次按键按下时,都记录一下最后停留位置
+                        that.$nextTick(function () {
+                            that.lastEditRange = getSelection().getRangeAt(0);
+                        });
+                    },
+                    click: function (e, editor) {
+                        //添加事件,在每次鼠标点击时,都记录一下最后停留位置
+                        that.$nextTick(function () {
+                            that.lastEditRange = getSelection().getRangeAt(0);
+                        });
+                    },
+                    //内容改变事件
+                    contentChanged: function () {
+                        that.lastEditRange = getSelection().getRangeAt(0) || 0;
+                        
+                        // that.$emit('textChange', that.html)
+                        that.sendHtml()
+                    },
+                },
+            }
+        }
+    },
+    methods: {
+        sendHtml(){
+            const str=this.html.replace(/<p data-f-id=\"pbf\".*?<\/p>/g, "")
+            this.$emit('textChange', str)
+        }
+    },
+    components:{}
+}
+</script>
+
+<style>
+
+</style>

+ 1274 - 0
src/views/smartReport/editReport.vue

@@ -0,0 +1,1274 @@
+<template>
+    <div class="edit-smart-report-page">
+        <!-- 顶部操作栏 -->
+        <div class="top-action-wrap">
+            <div class="title">{{reportInfo&&reportInfo.Title}}</div>
+            <ul class="action-list">
+                <li class="action-item" @click="showReportBaseInfo=true">
+                    <img src="~@/assets/img/smartReport/icon01.png" alt="">
+                    <span>基础信息</span>
+                </li>
+                <li class="action-item" @click="handleRefreshAllChart">
+                    <img src="~@/assets/img/smartReport/icon02.png" alt="">
+                    <span>图表刷新 </span>
+                </li>
+                <li class="action-item" @click="handlePreviewReport">
+                    <img src="~@/assets/img/smartReport/icon03.png" alt="">
+                    <span>预览</span>
+                </li>
+                <li class="action-item" @click="handleSaveContent">
+                    <img src="~@/assets/img/smartReport/icon01.png" alt="">
+                    <span>存草稿</span>
+                </li>
+                <li class="action-item" @click="handlePublishOpt('dsfb')">
+                    <img src="~@/assets/img/smartReport/icon01.png" alt="">
+                    <span>定时发布</span>
+                </li>
+                <li class="action-item" @click="handlePublishOpt('fb')">
+                    <img src="~@/assets/img/smartReport/icon01.png" alt="">
+                    <span>发布</span>
+                </li>
+            </ul>
+        </div>
+        <div class="main-wrap">
+            <div class="report-action-wrap">
+                <ul class="top-type-list">
+                    <li class="item" v-for="item in topTypeList" :key="item.name" @click="handleShowRight(item)">
+                        <img class="icon" :src="item.icon" alt="">
+                        <span>{{item.name}}</span>
+                    </li>
+                </ul>
+                <!-- 公共组件 -->
+                <draggable
+                    :list="compList"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="report-comp-wrap"
+                    animation="300"
+                    :sort="false"
+                    tag="ul"
+                >
+                    <li class="comp-item" :comp-data="JSON.stringify(comp)" v-for="comp in compList" :key="comp.id">
+                        <img :src="comp.icon">
+                    </li>
+                </draggable>
+
+                <div class="report-content-box">
+                    <draggable
+                        :list="conList"
+                        :group="{ name: 'component', pull: true, put: true }"
+                        class="report-html-wrap"
+                        animation="300"
+                        tag="div"
+                        handle=".drag-btn_p"
+                        @add="handleParentAdd"
+                        @remove="handleParentRemove"
+                        :move="handleParentMove"
+                    >
+                        <div 
+                            :class="['report-drag-item-wrap',activeId===item.id?'blue-bg':'']" 
+                            v-for="item,index in conList" 
+                            :key="item.id"
+                            :comp-type="item.compType"
+                            @click="handleChoose(item,index)"
+                            :style="item.style"
+                        >
+                            <!-- 缩放的盒子 -->
+                            <div class="resize-drag-box" @mousedown.stop="handleResizeP($event,index)"></div>
+                            <div class="opt-btn-box" style="display: none;">
+                                <div class="drag-btn drag-btn_p"></div>
+                                <div class="del-btn" @click.stop="handleDelItem(index,-1)"></div>
+                            </div>
+                            <div 
+                                v-if="item.child&&!item.child.length"
+                                class="report-drag-item-wrap_content"
+                                :data-id="item.id"
+                            >
+                                <component :is="getComponentName(item)" :compData="item"/>
+                            </div>
+                            <draggable
+                                :list="item.child"
+                                :group="{ name: 'component', pull: true, put: item.child&&item.child.length<3?true:false }"
+                                animation="300"
+                                tag="div"
+                                class="report-drag-item-wrap_child-wrap"
+                                @add="handleChildAdd($event,item,index)"
+                                @remove="handleChildRemove($event,item.child)"
+                                handle=".drag-btn_c"
+                                style="display: flex;gap: 20px;align-items: flex-start;"
+                            >
+                                <div 
+                                    :class="['report-drag-item-wrap_child_content',activeId===child.id?'blue-bg':'']" 
+                                    v-for="child,cindex in item.child" 
+                                    :key="child.id"
+                                    :comp-type="child.compType"
+                                    :data-id="child.id"
+                                    @click.stop="handleChoose(child,index,cindex)"
+                                    style="flex:1"
+                                    :style="child.style"
+                                >
+                                    <div class="opt-btn-box2" style="display: none;">
+                                        <div class="drag-btn drag-btn_c"></div>
+                                        <div class="del-btn" @click.stop="handleDelItem(index,cindex)"></div>
+                                    </div>
+                                    <!-- 拖动按钮 -->
+                                    <div class="resize-drag-box_lb" @mousedown.stop="handleResizeC($event,index,cindex,'lb')"></div>
+                                    <div class="resize-drag-box_rb" @mousedown.stop="handleResizeC($event,index,cindex,'rb')"></div>
+                                    <component :is="getComponentName(child)" :compData="child"/>
+                                </div>
+                            </draggable>
+                        </div>
+                    </draggable>
+                </div>
+            </div>
+
+            <div class="right-action-wrap" v-show="showRight">
+                <div class="close-icon" @click="handleCloseRight">
+                    <img src="~@/assets/img/smartReport/icon14.png" alt="">
+                </div>
+                <div style="overflow-x:auto">
+                    <div style="min-width:600px">
+                <TextEdit 
+                    v-if="rightType==='text'"
+                    :key="activeId"
+                    :content="activeContent" 
+                    @textChange="handleTextChange" 
+                />
+                <ImgEdit 
+                    v-if="rightType==='img'"
+                    :key="activeId"
+                    :content="activeContent" 
+                    @imgChange="handleTextChange" 
+                />
+                <!-- 图库插入 -->
+                <ETAChart 
+                    v-if="rightType==='etaChart'"
+                    @handleImportMyChart="handleImportMyChart"
+                />
+                <!-- ETA表格 -->
+                <ETASheet v-if="rightType==='etaSheet'"/>
+                <!-- 统计分析 -->
+                <StatisticAnalysis v-if="rightType==='statisticAnalysis'"/>
+                <!-- 商品价格曲线 -->
+                <ETAPriceChart v-if="rightType==='etaPriceChart'"/>
+                <!-- 沙盘图 -->
+                <ETASandBox v-if="rightType==='etaSandBox'"/>
+                <!-- 语义分析 -->
+                <SemanticAnalysis v-if="rightType==='semanticAnalysis'"/>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <!-- 报告基础信息 -->
+        <BaseInfo  v-model="showReportBaseInfo" :id="$route.query.id" @save="handleReportEdit" />
+
+        <!-- 定时发布弹窗 -->
+		<el-dialog 
+			v-dialogDrag 
+			:append-to-body="true" 
+			:visible.sync="showDSFB" 
+			width="500px" 
+			title="定时发布"
+		>
+			<div>
+				<div>
+					<span>发送时间</span>
+					<el-date-picker
+						v-model="taskTime"
+						type="datetime"
+						placeholder="选择日期时间"
+						value-format="yyyy-MM-dd HH:mm"
+						:picker-options="timePickerOpt"
+					/>
+				</div>
+				<p style="margin:15px 0">设置成功之后,研报将定时进行发送。</p>
+				<div style="text-align:right;margin:20px 0">
+					<el-button type="primary" plain @click="showDSFB=false">取消</el-button>
+					<el-button type="primary" @click="handleSetReportPrepublish">确定</el-button>
+				</div>
+			</div>
+		</el-dialog>
+
+    </div>
+</template>
+
+<script>
+import draggable from 'vuedraggable'
+import TextComp from './components/TextComp.vue'
+import ChartComp from './components/ChartComp.vue'
+import ImgComp from './components/ImgComp.vue'
+import SheetComp from './components/SheetComp.vue'
+import _ from 'lodash'
+import TextEdit from './components/TextEdit.vue'
+import ImgEdit from './components/ImgEdit.vue'
+import ETAChart from './components/ETAChart.vue'
+import ETASheet from './components/ETASheet.vue'
+import { getPublicSettingsApi } from '@/api/modules/oldApi';
+import { dataBaseInterface } from "@/api/api.js";
+import {apiSmartReport}  from '@/api/modules/smartReport'
+import BaseInfo from './components/BaseInfo.vue'
+import StatisticAnalysis from './components/StatisticAnalysis.vue'
+import ETAPriceChart from './components/ETAPriceChart.vue'
+import ETASandBox from './components/ETASandBox.vue'
+import SemanticAnalysis from './components/SemanticAnalysis.vue'
+import { getUrlParams } from '@/utils/common'
+export default {
+    name:"smartReportEdit",
+    components: {
+        draggable,
+        BaseInfo,
+        TextComp,
+        ChartComp,
+        ImgComp,
+        SheetComp,
+        TextEdit,
+        ImgEdit,
+        ETAChart,
+        ETASheet,
+        StatisticAnalysis,
+        ETAPriceChart,
+        ETASandBox,
+        SemanticAnalysis
+    },
+    watch:{
+        'taskTime'(){
+            this.taskTime=this.$moment(this.taskTime).format('YYYY-MM-DD HH:mm')+':00'
+			const date = this.$moment(this.taskTime).startOf('day').format('x');
+	        const nowDate = this.$moment().startOf('day').format('x');
+	        // 如果选择的是今天 则需要禁用已经过去的时间节点
+	        if (date <= nowDate) {
+	            // 默认选择的最新时间 是当前时间的两分钟后 (留出2分钟的富裕时间)
+	            this.timePickerOpt.selectableRange = (
+	                `${this.$moment().add(2,'m').format('HH:mm:ss')} - 23:59:59`
+	            );
+	        }else {
+				// 如果是以后的日期,则不需要禁用时间节点
+	            this.timePickerOpt.selectableRange = '00:00:00 - 23:59:59';
+	        }
+		},
+        conList:{
+            handler(n,o){
+                console.log('内容改变');
+                this.contentChange=true
+            },
+            deep:true
+        }
+    },
+    data() {
+        return {
+            reportInfo:null,
+            showReportBaseInfo:false,
+
+            topTypeList:[
+                {
+                    name:'图库',
+                    type:'etaChart',
+                    icon:require('@/assets/img/smartReport/icon04.png')
+                },
+                {
+                    name:'ETA表格',
+                    type:'etaSheet',
+                    icon:require('@/assets/img/smartReport/icon05.png')
+                },
+                {
+                    name:'统计分析',
+                    type:'statisticAnalysis',
+                    icon:require('@/assets/img/smartReport/icon06.png')
+                },
+                {
+                    name:'商品价格曲线',
+                    type:'etaPriceChart',
+                    icon:require('@/assets/img/smartReport/icon07.png')
+                },
+                {
+                    name:'沙盘图',
+                    type:'etaSandBox',
+                    icon:require('@/assets/img/smartReport/icon08.png')
+                },
+                {
+                    name:'语义分析',
+                    type:'semanticAnalysis',
+                    icon:require('@/assets/img/smartReport/icon09.png')
+                }
+            ],
+            compList:[
+                {   
+                    compId:1,
+                    compType:'text',
+                    icon:require('@/assets/img/smartReport/icon10.png')
+                },
+                {
+                    compId:2,
+                    compType:'img',
+                    icon:require('@/assets/img/smartReport/icon11.png')
+                },
+                // {
+                //     compId:3,
+                //     compType:'chart',
+                // },
+                // {
+                //     compId:4,
+                //     compType:'sheet',
+                // }
+                
+            ],
+            conList:[],
+            activeId:'',
+            activeContent:'',
+            activePindex:'',
+            activeCindex:'',
+            showRight:false,
+            rightType:'',
+
+            timer:null,//自动保存定时器
+
+            showDSFB:false,//显示定时发布弹窗
+			taskTime:'',//定时发布的时间
+			timePickerOpt:{
+				disabledDate(e){
+					return e.getTime()< new Date().getTime()-24 * 60 * 60 * 1000
+				},
+				selectableRange:'00:00:00 - 23:59:59',
+                format:'HH:mm'
+			},
+
+            contentChange:false,//内容是否发生变化
+
+            isDragResize:false,//是否正在拖动缩放
+        }
+    },
+    methods: {
+        // 大盒子的高度缩放
+        handleResizeP(e,index){
+            this.isDragResize=true
+            e.preventDefault()
+            const targetBox=e.target.parentNode
+            const targetBoxHeight=targetBox.offsetHeight
+            const startY=e.clientY
+            document.onmousemove=(mouseEl)=>{
+                mouseEl.preventDefault()
+                const h=mouseEl.clientY-startY+targetBoxHeight
+                targetBox.style.minHeight=`${h<50?50:h}px`
+            }
+            document.onmouseup=(el)=>{
+                console.log(targetBox.style.cssText);
+                this.$set(this.conList[index],'style',targetBox.style.cssText)
+                el.preventDefault()
+                document.onmousemove=null
+                setTimeout(() => {
+                    this.isDragResize=false
+                }, 50);
+                
+            }
+        },
+        // 内部元素的缩放
+        handleResizeC(e,index,cindex,type){
+            this.isDragResize=true
+            e.preventDefault()
+            const parentBox=e.target.parentNode.parentNode
+            const parentBoxWidth=parentBox.offsetWidth
+            console.log(e);
+            const targetBox=e.target.parentNode
+            const targetBoxHeight=targetBox.offsetHeight
+            const targetBoxWidth=targetBox.offsetWidth
+            const startY=e.clientY
+            const startX=e.clientX
+            const initW=targetBoxWidth //拖动前要拖动盒子所占的宽度
+
+            let computerW=0//存放其他兄弟节点要改变的宽度的值
+
+            document.onmousemove=(mouseEl)=>{
+                mouseEl.preventDefault()
+                const h=mouseEl.clientY-startY+targetBoxHeight
+                // 计算宽度
+                const w=type==='rb'?mouseEl.clientX-startX+targetBoxWidth:startX-mouseEl.clientX+targetBoxWidth
+                const resW= (w/parentBoxWidth)*100//计算出的百分比结果值
+                targetBox.style.width=resW+'%'
+                targetBox.style.flex='none'
+
+                // 处理兄弟盒子的宽度
+                const changeW=w-initW //宽度变化值
+                computerW=changeW
+                if(parentBox.childNodes.length===3){
+                    computerW=changeW/2
+                }
+                // console.log('改变的宽度',computerW);
+
+                targetBox.style.height=`${h<50?50:h}px`
+            }   
+            document.onmouseup=(el)=>{
+                if(document.onmousemove){
+                    parentBox.childNodes.forEach(item=>{
+                        if(item!==targetBox){
+                            const temw=item.offsetWidth-computerW
+                            item.style.width=((temw/parentBoxWidth)*100)+'%'
+                        }
+                    })
+                    // 存储修改的值
+                    parentBox.childNodes.forEach((item,idx)=>{
+                        this.$set(this.conList[index].child[idx],'style',item.style.cssText)
+                    })
+                }
+                el.preventDefault()
+                document.onmousemove=null
+                setTimeout(() => {
+                    this.isDragResize=false
+                    document.onmouseup=null
+                }, 50);
+            }
+        },
+
+        // 跳转预览
+        handlePreviewReport(){
+            const htmlStr=$('.report-html-wrap').html()
+            sessionStorage.setItem('smartReportContent', htmlStr);
+			let { href } = this.$router.resolve({ 
+                path: '/smartReportDetail',
+                query:{
+                    id:this.$route.query.id,
+                    type:'preview'
+                }
+            });
+			window.open(href, '_blank');
+        },
+
+        // 批量插入myETA数据
+        handleImportMyChart(list){
+            const LINK_CHART_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/chartshow';
+            // console.log(list);
+            let arr=[]
+            // 分成三个一组
+            for (let index = 0; index < list.length; index+=3) {
+                const temarr=list.slice(index,index+3)
+                let resArr={child:[]}
+                if(temarr.length===1){//落单了
+                    resArr={
+                        compId:3,
+                        compType:'chart',
+                        content:`${LINK_CHART_URL}?code=${temarr[0].UniqueCode}`,
+                        id:this.getCompId(`chart${temarr[0].UniqueCode}_`),
+                        child:[]
+                    }
+                }else{
+                    temarr.forEach(e => {
+                        resArr.child.push({
+                            compId:3,
+                            compType:'chart',
+                            content:`${LINK_CHART_URL}?code=${e.UniqueCode}`,
+                            id:this.getCompId(`chart${e.UniqueCode}_`),
+                            child:[]
+                        })
+                    });
+                }
+                arr.push(resArr)
+            }
+            // console.log(arr);
+            this.conList=[...this.conList,...arr]
+            
+        },
+
+        // 设置sheet iframe 样式
+        setSheetIframeStyle(e){
+            const { height, code } = e.data;
+            // console.log(e.data);
+            let iframeDom = document.getElementsByClassName(`iframe${code}`);
+            // console.log(iframeDom);
+            iframeDom.forEach((ele) => {
+                ele.height = `${height + 45}px`;
+            });
+        },
+
+        handleParentAdd(e){
+            console.log('container-onAdd操作------------------->');
+
+            const {item,newDraggableIndex}=e
+
+            const hasid=item.getAttribute('data-id')
+            console.log(hasid);
+
+            this.conList.forEach(item=>{
+                if(item.id==hasid&&item.style){
+                    const styleArr=item.style.split(';').filter(s=>s&&(s.indexOf('width')===-1&&s.indexOf('flex')===-1)).join(';')
+                    item.style=styleArr
+                }
+            })
+            if(hasid) return
+
+            // 要添加的元素数据
+            const compData=JSON.parse(item.getAttribute('comp-data'))
+            // console.log(compData);
+            // 非注册组件返回
+            // if(!comp){
+            //     this.conList.splice(newDraggableIndex,1)
+            //     return
+            // }
+            const tempCompData={
+                compId:compData.compId,
+                compType:compData.compType,
+                id:this.getCompId(compData.compType),
+                content:compData.content||'',
+                child:[]
+            }
+            // console.log(tempCompData);
+            this.conList.splice(newDraggableIndex,1,tempCompData)
+            // this.activeId=tempCompData.id
+        },
+
+        handleChildAdd(e,parent,parentIndex){
+            console.log('child-onAdd操作------------------->');
+            // console.log(parent);
+            const {item,newDraggableIndex}=e
+
+            const compData=JSON.parse(item.getAttribute('comp-data'))
+
+            console.log(compData,newDraggableIndex);
+
+            const index=parentIndex
+            // if(index>-1){
+                let obj=_.cloneDeep(this.conList[index]) 
+
+                console.log(obj);
+
+                if(obj.child&&obj.child.length===1&&obj.id){
+                    if(compData){
+                        obj={
+                            child:[
+                                {
+                                    compId:obj.compId,
+                                    compType:obj.compType,
+                                    id:obj.id,
+                                    content:obj.content,
+                                    child:[]
+                                },
+                                {
+                                    compId:compData.compId,
+                                    compType:compData.compType,
+                                    content:compData.content||'',
+                                    id:this.getCompId(compData.compType),
+                                    child:[]
+                                }
+                            ]
+                        }
+                    }else{//是内容区域拖动排序的
+                        const temItem=_.cloneDeep(obj.child[0])
+                        if(temItem.child.length>0){//如果拖动的盒子里面有子元素则不能进入
+                            obj={
+                                ...obj,
+                                child:[]
+                            }
+                            setTimeout(() => {
+                                this.conList.splice(index,0,temItem)
+                            }, 50);
+                        }else{
+                            obj={
+                                child:[
+                                    {
+                                        compId:obj.compId,
+                                        compType:obj.compType,
+                                        id:obj.id,
+                                        content:obj.content,
+                                        child:[]
+                                    },
+                                    {
+                                        ...temItem
+                                    }
+                                ]
+                            }
+                        }
+                    }
+                    
+                }else{
+                    if(compData){//如果是从内容区域拖入的没有compData
+                        obj.child.splice(newDraggableIndex,1,{
+                            compId:compData.compId,
+                            compType:compData.compType,
+                            content:compData.content||'',
+                            id:this.getCompId(compData.compType),
+                            child:[]
+                        })
+                    }
+                    
+                }
+                console.log(obj);
+
+                this.conList.splice(index,1,obj)
+            // }
+        },
+
+        handleParentMove(e){
+            // console.log(e.draggedContext);
+            // console.log(e.relatedContext);
+            // // console.log(e.relatedContext.compType);
+            // console.log(e.draggedContext.element.child.length>0&&!e.relatedContext.compType);
+            // if(e.draggedContext.element.child.length>0&&(!e.relatedContext.compType||e.relatedContext.element.compType)) return false
+        },
+
+        // 移除事件 
+        handleChildRemove(e,arr){
+            console.log('child-remove操作------------------->');
+            // 如果都移除了则删除这个
+            // this.conList=this.conList.filter(_item=>!(_item.child&&_item.child.length===0&&!_item.id))
+
+            // 如果当前移出的这个child还有两个的话则重置他们的宽度
+            arr.forEach(item=>{
+                if(item.style){
+                    const styleArr=item.style.split(';').filter(s=>s&&(s.indexOf('width')===-1&&s.indexOf('flex')===-1)).join(';')
+                    item.style=styleArr
+                }
+            })
+
+
+            // 如果child只剩一个了则移出来
+            this.conList=this.conList.map(_item=>{
+                if(_item.child&&_item.child.length===1){
+                    const obj=_item.child[0]
+                    if(obj.style){
+                        const styleArr=obj.style.split(';').filter(s=>s&&(s.indexOf('width')===-1&&s.indexOf('flex')===-1)).join(';')
+                        obj.style=styleArr
+                    }
+                    return obj
+                }else{
+                    return _item
+                }
+            })
+        },
+
+        handleParentRemove(e){
+            console.log('container-remove操作------------------->');
+        },
+
+        // 点击删除某个
+        handleDelItem(pindex,cindex){
+            if(cindex===-1){
+                this.conList.splice(pindex,1)
+            }else{//删除子盒子
+                this.conList[pindex].child.splice(cindex,1)
+                if(this.conList[pindex].child.length===1){//只剩一个子盒子了则变成一个大盒子
+                    this.conList[pindex]=this.conList[pindex].child[0]
+                    this.conList[pindex].style='flex:1'
+                }
+            }
+        },
+
+        // 富文本编辑组件/图片组件数据变化
+        handleTextChange(e){
+            console.log(e);
+            this.activeContent=e
+            if(this.activeCindex>=0&&this.activeCindex!==''){
+                if(this.conList[this.activePindex].child[this.activeCindex].content){
+                    this.conList[this.activePindex].child[this.activeCindex].content=e
+                }else{
+                    this.$set(this.conList[this.activePindex].child[this.activeCindex],'content',e)
+                }
+            }else{
+                if(this.conList[this.activePindex].content){
+                    this.conList[this.activePindex].content=e
+                }else{
+                    this.$set(this.conList[this.activePindex],'content',e)
+                }
+            }
+        },
+
+        // 当前再编辑哪个
+        handleChoose(item,index,cindex){
+            //{item:数据,index:父序号,cindex:子序号}
+            if(!item.id||this.isDragResize||!['text','img'].includes(item.compType)) return
+            this.activeId=item.id
+            this.showRight=true
+            this.rightType=item.compType
+            this.activeContent=item.content||''
+            this.activePindex=index
+            this.activeCindex=cindex>=0?cindex:''
+        },
+
+        //点击顶部插入图或者图表等类型
+        handleShowRight(item){
+            this.rightType=item.type
+            this.activeId=''
+            this.activeContent=''
+            this.activePindex=''
+            this.activeCindex=''
+            this.showRight=true 
+        },
+
+        // 关闭右侧
+        handleCloseRight(){
+            this.activeId=''
+            this.activeContent=''
+            this.activePindex=''
+            this.activeCindex=''
+            this.showRight=false
+            this.rightType=''
+        },
+
+        getComponentName(item){
+            const temMap=new Map([
+                ['text',TextComp],
+                ['chart',ChartComp],
+                ['img',ImgComp],
+                ['sheet',SheetComp]
+            ])
+            return temMap.get(item.compType)
+        },
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        // 编辑保存报告
+        handleReportEdit(e){
+            const params={
+                SmartReportId:Number(this.$route.query.id)||0,
+                ...e,
+                Content:$('.report-html-wrap').html(),
+                ContentStruct:JSON.stringify(this.conList)
+            }
+            console.log(params);
+            apiSmartReport.reportEdit({...params}).then(res=>{
+                if(res.Ret===200){
+                    this.$message.success('保存成功')
+                    this.reportInfo.Title=params.Title
+                    this.showReportBaseInfo=false
+                }
+            })
+        },
+
+        /* 获取动态配置 外部动态链接 */
+        async getPublicSettings() {
+            const res =  await getPublicSettingsApi();
+            if(res.Ret !== 200) return
+            
+            this.$store.commit('SET_DYNAMIC_LINK',res.Data)
+        },
+
+        // 获取报告详情
+        getReportDetail(){
+            const id=this.$route.query.id||0
+            if(!id) return
+            apiSmartReport.reportDetail({
+                SmartReportId:Number(id)
+            }).then(res=>{
+                if(res.Ret===200){
+                    this.reportInfo=res.Data
+                    this.conList=res.Data.ContentStruct?JSON.parse(res.Data.ContentStruct):[]
+                    this.$nextTick(()=>{
+                        this.contentChange=false
+                    })
+                }
+            })
+        },
+
+        // 刷新所有图表
+        handleRefreshAllChart: _.debounce ( async function() {
+            let code_arr = [];
+            $('iframe').each((k,i) => {
+                try {
+                    let href = $(i).attr('src');
+                    code_arr.push(getUrlParams(href,'code'));
+                } catch (err) {
+                }
+            });
+
+            if(!code_arr.length) return this.$message.warning('请插入图表');
+
+            const loading = this.$loading({
+                lock: true,
+                text: '刷新中..',
+                spinner: 'el-icon-loading',
+                background: 'rgba(0, 0, 0, 0.02)'
+            });
+
+            const { Ret } = await dataBaseInterface.reportRefresh({
+                ChartInfoCode: code_arr
+            })
+        
+            loading.close();
+            
+            if(Ret === 200) {
+                $('iframe').each((k,i) => {
+                    $(i).attr('src',$(i).attr('src'))
+                });
+                this.$message.success('刷新成功');
+            }
+
+        },1000),
+        
+        // 自动/存草稿保存内容
+        handleSaveContent({isAutoSave}){
+            return new Promise((resolve,reject)=>{
+                const id=this.$route.query.id||0
+                if(!id) return
+                apiSmartReport.saveReportContent({
+                    SmartReportId:Number(id),
+                    Content:$('.report-html-wrap').html(),
+                    ContentStruct:JSON.stringify(this.conList),
+                    NoChange:this.contentChange?2:1,
+                }).then(res=>{
+                    if(res.Ret===200){
+                        resolve(true)
+                        if(!isAutoSave){
+                            this.$message.success('保存成功')
+                        }
+                    }
+                    if(res.Msg==='报告已发布, 不允许编辑'){
+                        this.$router.replace({ path: '/smartReportList' });
+                    }
+                })
+            })
+        },
+
+        // 点击定时发布/发布
+        async handlePublishOpt(type){
+            // 存一次草稿
+            const saveRes=await this.handleSaveContent({isAutoSave:true})
+            if(!saveRes) return
+            if(type==='dsfb'){
+                this.showDSFB=true
+                return
+            }
+            // 发布
+            if(this.reportInfo.MsgIsSend==1){//该报告已经推送过模板消息
+                this.reportPublish({sendMsg:false})
+            }else{
+                const isPost = this.permissionBtn.checkPermissionBtn(this.permissionBtn.smartReportManageBtn.reportManage_sendMsg)
+                this.$confirm(
+                    isPost?'发布后,是否推送模板消息?':'是否立即发布报告?', 
+                    '发布提示', 
+                    {
+                        confirmButtonText: isPost?'推送':'发布',
+                        cancelButtonText: isPost?'不推送':'取消',
+                        type: 'warning',
+                        distinguishCancelAndClose:true,
+                    
+                        beforeClose:(action, instance,done)=>{
+                            if(action==='close') {
+                                //右上角
+                                // this.isPublishloading = false;
+                            } else if(action==='cancel') {
+                                //cancelButton
+                                if(isPost){
+                                    this.reportPublish({sendMsg:false})
+                                }
+                            }else {
+                                //confirmButton
+                                this.reportPublish({sendMsg:isPost?true:false})
+                            }
+                            done()
+                        }
+                    }
+                )
+            }
+        },
+
+        // 定时发布报告
+        async handleSetReportPrepublish(){
+            if(!this.taskTime){
+				this.$message.warning('请选择定时发布的时间')
+				return
+			}
+			const now=this.$moment().format('YYYY-MM-DD HH:mm:ss')
+			// console.log(now);
+			// console.log(this.taskTime);
+			if(this.$moment(this.taskTime).isBefore(now,'second')){
+				this.$message.warning('定时发布不得早于当前时间')
+				return
+			}
+            // 如果该报告已经推送过模板消息
+            if(this.reportInfo.MsgIsSend==1){
+                apiSmartReport.prePublishReport({
+                    SmartReportId:Number(this.$route.query.id),
+                    PrePublishTime:this.taskTime,
+                    PreMsgSend:0
+                }).then(res=>{
+                    if(res.Ret===200){
+                        this.$message.success('定时发布成功!')
+                        this.$router.replace({ path: '/smartReportList' });
+                    }
+                })
+                return
+            }
+
+            const isPost = this.permissionBtn.checkPermissionBtn(this.permissionBtn.smartReportManageBtn.reportManage_sendMsg)
+
+            this.$confirm(isPost?'是否发布定时报告,并推送模板消息?':'是否发布定时报告?', '发布提示', {
+				confirmButtonText: isPost?'推送':'发布',
+				cancelButtonText: isPost?'不推送':'取消',
+				type: 'warning',
+				distinguishCancelAndClose:true,
+				beforeClose:(action, instance,done)=>{
+					console.log(action, instance);
+					if(action==='close'||action==='cancel') {
+						//右上角或者不推送
+						if(isPost){
+							apiSmartReport.prePublishReport({
+								SmartReportId:Number(this.$route.query.id),
+								PrePublishTime:this.taskTime,
+								PreMsgSend:0,
+							}).then(res=>{
+								if(res.Ret===200){
+									this.$message.success('定时发布成功!')
+									this.$router.replace({ path: '/smartReportList' });
+								}
+							})
+						}
+					} else {
+						//confirmButton
+						apiSmartReport.prePublishReport({
+							SmartReportId:Number(this.$route.query.id),
+							PrePublishTime:this.taskTime,
+							PreMsgSend:isPost?1:0,
+						}).then(res=>{
+							if(res.Ret===200){
+								this.$message.success('定时发布成功!')
+								this.$router.replace({ path: '/smartReportList' });
+							}
+						})
+					}
+					done()
+				}
+			})
+        },
+
+        // 发布报告
+        reportPublish({sendMsg}){
+            apiSmartReport.publishReport({
+                SmartReportId:Number(this.$route.query.id),
+                PublishState:2
+            }).then(res=>{
+                if(res.Ret===200){
+                    if(sendMsg){
+                        this.reportSendMsg()
+                    }
+                    this.$router.replace({ path: '/smartReportList' });
+                }
+            })
+        },
+
+        //报告消息推送
+        reportSendMsg(){
+            apiSmartReport.reportMsgSend({SmartReportId:Number(this.$route.query.id)}).then(res=>{})
+        }
+    },
+    created() {
+        this.getPublicSettings()
+        this.getReportDetail()
+    },
+    mounted () {
+        window.addEventListener('message',this.setSheetIframeStyle)
+        this.timer = setInterval(() => {
+			this.handleSaveContent({isAutoSave:true});
+		}, 6000);
+    },
+    destroyed() {
+		window.removeEventListener('message',this.setSheetIframeStyle)
+        if (this.timer) {
+			clearInterval(this.timer);
+		}
+	},
+}
+</script>
+<style lang='scss' scoped>
+div{
+    box-sizing: border-box;
+}
+// .sortable-ghost{
+//     height: 5px !important;
+//     background-color: #0052D9 !important;
+//     overflow: hidden !important;
+//     padding: 0 !important;
+//     min-height: 0 !important;
+//     border: none !important;
+// }
+.edit-smart-report-page{
+    background: var(--unnamed, #F2F6FA);
+    min-width: 100vw;
+    min-height: 100vh;
+    .top-action-wrap{
+        position: sticky;
+        top: 0px;
+        background-color: #fff;
+        box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.10);
+        height: 60px;
+        display: flex;
+        justify-content: space-between;
+        align-content: center;
+        padding: 0 24px;
+        .title{
+            line-height: 60px;
+            font-size: 16px;
+        }
+        .action-list{
+            display: flex;
+            align-items: center;
+            .action-item{
+                height: 36px;
+                display: flex;
+                align-items: center;
+                border-right: 1px solid #C8CDD9;
+                padding: 0 30px;
+                font-size: 16px;
+                cursor: pointer;
+                img{
+                    width: 16px;
+                    height: 16px;
+                    margin-right: 4px;
+                    position: relative;
+                    top: 2px;
+                }
+                &:last-child{
+                    border-right: none;
+                }
+            }
+        }
+    }
+    .main-wrap{
+        display: flex;
+        justify-content: center;
+    }
+}
+.report-action-wrap{
+    width: 800px;
+    flex-shrink: 0;
+    margin-left: 90px;
+    margin-right: 60px;
+    margin-top: 30px;
+    position: relative;
+    .report-comp-wrap{
+            position: absolute;
+            left: -62px;
+            top: 72px;
+            width: 40px;
+            border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+            background: var(--gary-gy-white, #FFF);
+            border-radius: 4px;
+            overflow: hidden;
+            .comp-item{
+                cursor: move;
+                height: 40px;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                border-bottom: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                &:last-child{
+                    border-bottom: none;
+                }
+                img{
+                    width: 16px;
+                }
+            }
+    }
+    .top-type-list{
+        margin-bottom: 20px;
+        display: flex;
+        align-items: center;
+        background-color: #fff;
+        height: 50px;
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        .item{
+            cursor: pointer;
+            flex: auto;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            border-right: 1px solid #C8CDD9;
+            font-size: 16px;
+            &:last-child{
+                border-right: none;
+            }
+            .icon{
+                width: 20px;
+                height: 20px;
+                margin-right: 4px;
+            }
+        }
+    }
+    .report-content-box{
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px 20px 20px 64px;
+        height: calc(100vh - 180px);
+        position: relative;
+        overflow-y: auto;
+        
+    }
+}
+
+
+
+.main-wrap{
+    .report-html-wrap{
+        min-height: 100%;
+        .report-drag-item-wrap{
+            width: 100%;
+            padding: 20px;
+            min-height: 80px;
+            border: 1px dashed #0052D9;
+            position: relative;
+            margin-bottom: 10px;
+            &:hover{
+                border-style: solid;
+                .opt-btn-box{
+                    display: block !important;
+                }
+                .resize-drag-box{
+                    display: block;
+                }
+            }
+            .opt-btn-box{
+                position: absolute;
+                left: -36px;
+                padding-right: 8px;
+                top: 0;
+                .drag-btn::after{
+                    content: '';
+                    display: block;
+                    width: 28px;
+                    height: 28px;
+                    background-image: url('~@/assets/img/smartReport/icon12.png');
+                    background-size: cover;
+                    cursor: pointer;
+                }
+                .del-btn::after{
+                    content: '';
+                    display: block;
+                    width: 28px;
+                    height: 28px;
+                    background-image: url('~@/assets/img/smartReport/icon13.png');
+                    background-size: cover;
+                    cursor: pointer;
+                }
+            }
+            .resize-drag-box{
+                position: absolute;
+                right: -4px;
+                bottom: -4px;
+                width: 8px;
+                height: 8px;
+                display: none;
+                border: 1px solid #0052D9;
+                cursor: n-resize;
+            }
+        }
+        .report-drag-item-wrap_child-wrap{
+            min-height: 30px;
+        }
+        .report-drag-item-wrap_child_content{
+            min-height: 80px;
+            border: 1px dashed #0052D9;
+            flex: 1;
+            position: relative;
+            &:hover{
+                border-style: solid;
+                .opt-btn-box2{
+                    display: block !important;
+                }
+                .resize-drag-box_lt,
+                .resize-drag-box_lb,
+                .resize-drag-box_rt,
+                .resize-drag-box_rb{
+                    display: block;
+                }
+            }
+            .opt-btn-box2{
+                position: absolute;
+                right: -15px;
+                top: 0;
+                .drag-btn::after{
+                    content: '';
+                    display: block;
+                    width: 28px;
+                    height: 28px;
+                    background-image: url('~@/assets/img/smartReport/icon12.png');
+                    background-size: cover;
+                    cursor: pointer;
+                }
+                .del-btn::after{
+                    content: '';
+                    display: block;
+                    width: 28px;
+                    height: 28px;
+                    background-image: url('~@/assets/img/smartReport/icon13.png');
+                    background-size: cover;
+                    cursor: pointer;
+                }
+            }
+            .resize-drag-box_lt,
+            .resize-drag-box_lb,
+            .resize-drag-box_rt,
+            .resize-drag-box_rb{
+                position: absolute;
+                width: 8px;
+                height: 8px;
+                display: none;
+                border: 1px solid #0052D9;
+            }
+            .resize-drag-box_lt{
+                left: -4px;
+                top: -4px;
+                cursor: nw-resize;
+            }
+            .resize-drag-box_lb{
+                left: -4px;
+                bottom: -4px;
+                cursor: ne-resize;
+            }
+            .resize-drag-box_rt{
+                right: -4px;
+                top: -4px;
+                cursor: ne-resize;
+            }
+            .resize-drag-box_rb{
+                right: -4px;
+                bottom: -4px;
+                cursor: nw-resize;
+            }
+        }
+        .blue-bg{
+            background: var(--brand-brand-1-light, #ECF2FE);
+        }
+    }
+}
+
+.main-wrap{
+    .right-action-wrap{
+        height: calc(100vh - 60px);
+        overflow: hidden;
+        flex: 1;
+        position: relative;
+        padding-top: 30px;
+        padding-left: 75px;
+        padding-right: 30px;
+        padding-bottom: 30px;
+        &::before{
+            display: block;
+            content: '';
+            width: 1px;
+            height: 100vh;
+            background-color: #C8CDD9;
+            position: absolute;
+            left: 15px;
+            top: 0;
+        }
+        .close-icon{
+            width: 20px;
+            height: 20px;
+            border-radius: 4px;
+            background: #FFF;
+            box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            cursor: pointer;
+            position: absolute;
+            left: 5px;
+            top: 22px;
+            img{
+                width: 16px;
+                height: 16px;
+            }
+        }
+    }
+}
+</style>

+ 239 - 0
src/views/smartReport/reportDetail.vue

@@ -0,0 +1,239 @@
+<template>
+    <div class="smart-report-detail">
+        <div class="main-box">
+            <div class="top-box">
+                <div class="title">{{reportInfo&&reportInfo.Title}}</div>
+                <div class="flex">
+                    <span>{{reportInfo&&reportInfo.Author}}</span>
+                    <span>{{reportInfo&&reportInfo.PublishTime}}</span>
+                </div>
+            </div>
+            <div class="html-wrap" v-html="content"></div>
+        </div>
+        <div class="right-opt-box" v-if="$route.query.type!=='preview'">
+            <div 
+                class="item copy" 
+                v-if="linkUrl" 
+                :data-clipboard-text='linkUrl' 
+                @click="copyHandle"
+                v-permission="permissionBtn.smartReportManageBtn.reportManage_reportView_copyWechat"
+            >
+                <img src="~@/assets/img/icons/cop.png" alt="" style="width:30px;height:30px;marginRight:10px;">
+                <span>复制链接</span>
+            </div>
+            <div 
+                class="item wechat-item" 
+                v-if="linkUrl"
+                v-permission="permissionBtn.smartReportManageBtn.reportManage_reportView_copyWechat"
+            >
+                <img src="~@/assets/img/icons/wechat.png" alt="" style="width:30px;height:30px;marginRight:10px;">
+                <span>微信分享</span>
+                <vue-qr 
+                    :text="linkUrl" 
+                    :margin="0" 
+                    colorDark="#333" 
+                    colorLight="#fff" 
+                    :dotScale="1" 
+                    :size="100" 
+                    class="qrcode"
+                ></vue-qr>
+            </div>
+            <div class="item" @click="handleGetReportImg" v-permission="permissionBtn.smartReportManageBtn.reportManage_reportView_exportImg">
+                <img src="~@/assets/img/smartReport/icon16.png" alt="" style="width:30px;height:30px;marginRight:10px;">
+                <span>导&nbsp;&nbsp;图</span>
+            </div>
+        </div>
+
+        <!-- 报告图片 -->
+        <div class="report-img-box" v-if="showReportImg&&reportImgUrl" @click="showReportImg=false">
+            <img :src="reportImgUrl" alt="" @click.stop="">
+        </div>
+    </div>
+</template>
+
+<script>
+import {apiSmartReport}  from '@/api/modules/smartReport'
+import vueQr from 'vue-qr'
+export default {
+    computed: {
+		linkUrl(){
+			let str=''
+			const baseUrl= localStorage.getItem('dynamicOutLinks') ? JSON.parse(localStorage.getItem('dynamicOutLinks')).ReportViewUrl : '';
+			if(this.$route.query.code){
+				str=`${baseUrl}/reportshare_smart_report?code=${this.$route.query.code}`
+			}
+			return str
+		}
+	},
+    data() {
+        return {
+            reportInfo:null,
+            content:'',
+            reportImgUrl:'',
+            showReportImg:false
+        }
+    },
+    created() {
+        this.getReportDetail()
+    },
+    methods: {
+        // 获取报告详情
+        getReportDetail(){
+            const id=this.$route.query.id||0
+            if(!id) return
+            apiSmartReport.reportDetail({
+                SmartReportId:Number(id)
+            }).then(res=>{
+                if(res.Ret===200){
+                    this.reportInfo=res.Data
+                    if(this.$route.query.type==='preview'){
+                       this.content=sessionStorage.getItem('smartReportContent')
+                    }else{
+                        this.content=res.Data.Content
+                    }
+                }
+            })
+        },
+
+        /* 复制链接 */
+		copyHandle() {
+				var clipboard = new this.Clipboard('.copy')
+				clipboard.on('success', e => {
+					this.$message.success('复制链接成功')
+					e.clearSelection() // 释放内存
+					clipboard.destroy()
+				})
+				// // 浏览器不支持
+				clipboard.on('error', e => {
+					this.$message.warning('浏览器暂不支持')
+					// 释放内存
+					clipboard.destroy()
+				})
+		},
+
+        handleGetReportImg(){
+            // if(this.reportImgUrl){
+            //     this.showReportImg=true
+            //     return
+            // }
+            apiSmartReport.getReportImg({
+                SmartReportId:Number(this.$route.query.id)
+            }).then(res=>{
+                if(res.Ret===200){
+                    // this.reportImgUrl=res.Data
+                    // this.showReportImg=true
+                    // 改为直接下载图片不是展示
+                    this.saveImg(res.Data)
+                }else{
+                    if(res.Ret!==403){
+                        this.$message.warning(res.Msg)
+                    }
+                }
+            })
+        },
+
+        saveImg(imgUrl){
+            let img=new Image()
+            img.setAttribute('crossOrigin', 'anonymous');
+            img.src=imgUrl
+            img.onload=()=>{
+                let canvas = document.createElement("canvas");
+                canvas.width = img.width;
+                canvas.height = img.height;
+                let context = canvas.getContext('2d');
+                context.drawImage(img, 0, 0, img.width, img.height);
+                let dataURL = canvas.toDataURL("image/png", 1);
+                const a=document.createElement('a')
+                a.setAttribute("download",this.reportInfo.Title)
+                a.style.display = "none"
+                a.href=dataURL
+                document.body.appendChild(a);
+                a.click()
+            }
+            img.onerror=(e)=>{
+                console.log(e);
+                this.$message.warning("自动下载图片失败,请手动保存")
+                if(imgUrl){
+                    this.reportImgUrl=imgUrl
+                    this.showReportImg=true
+                }
+            }
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.smart-report-detail{
+    .right-opt-box{
+            position: fixed;
+            top: 100px;
+            left: calc(50% + 450px);
+            .item{
+                display: flex;
+                align-items: center;
+                cursor: pointer;
+                margin-bottom: 20px;
+                position: relative;
+                .qrcode{
+                    position: absolute;
+                    left: 110%;
+                    display: none !important;
+                    z-index: 9;
+                }
+            }
+            .wechat-item{
+                &:hover{
+                    .qrcode{
+                        display: inline-block !important;
+                    }
+                }
+            }
+    }
+    .main-box{
+        width: 800px;
+        margin: 0 auto;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        border-radius: 4px;
+        overflow: hidden;
+        position: relative;
+        .top-box{
+            width: 100%;
+            height: 160px;
+            background-image: url('~@/assets/img/smartReport/bg01.png');
+            background-size: cover;
+            background-repeat: no-repeat;
+            background-position: center;
+            padding: 40px;
+            color: #fff;
+            .title{
+                font-size: 20px;
+                margin-bottom: 20px;
+            }
+            .flex{
+                display: flex;
+                justify-content: space-between;
+            }
+        }
+        .html-wrap{
+            padding: 30px;
+        }
+    }
+    .report-img-box{
+        position: fixed;
+        left: 0;
+        top: 0;
+        bottom: 0;
+        right: 0;
+        overflow-y: auto;
+        background: rgba($color: #000000, $alpha: 0.6);
+        text-align: center;
+        img{
+            max-width: 800px;
+        }
+    }
+}
+</style>

+ 794 - 0
src/views/smartReport/reportList.vue

@@ -0,0 +1,794 @@
+<template>
+    <div class="smart-report-list">
+        <el-card class="box-card">
+            <div slot="header" class="header">
+                <el-form :inline="true" :model="searchform" @submit.native.prevent>
+                <el-form-item label="">
+                    <el-button
+                        v-permission="permissionBtn.smartReportManageBtn.reportManage_reportAdd"
+                        type="primary"
+                        size="medium"
+                        @click="showAddReport=true"
+                    >添加研报</el-button>
+                </el-form-item>
+                <el-form-item label="">
+                    <el-select
+                    v-model="searchform.timeType"
+                    placeholder="选择时间类型"
+                    size="medium"
+                    style="width:110px"
+                    @change="search"
+                    >
+                    <el-option label="发布时间" value="publish_time"></el-option>
+                    <el-option label="更新时间" value="modify_time"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="">
+                    <el-date-picker
+                        @change="search"
+                        v-model="searchform.dateValue"
+                        type="daterange"
+                        unlink-panels
+                        value-format="yyyy-MM-dd"
+                        range-separator="至"
+                        start-placeholder="起始日期"
+                        end-placeholder="结束日期"
+                        size="medium"
+                        style="width: 280px"
+                    />
+                </el-form-item>
+                <el-form-item label="">
+                    <el-select
+                        @change="search"
+                        v-model="searchform.frequency"
+                        placeholder="更新频度筛选"
+                        size="medium"
+                        clearable
+                        style="width: 140px"
+                    >
+                        <el-option label="年度" value="年度"></el-option>
+                        <el-option label="半年度" value="半年度"></el-option>
+                        <el-option label="季度" value="季度"></el-option>
+                        <el-option label="月度" value="月度"></el-option>
+                        <el-option label="双周度" value="双周度"></el-option>
+                        <el-option label="周度" value="周度"></el-option>
+                        <el-option label="日度" value="日度"></el-option>
+                        <el-option label="不定时" value="不定时"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="">
+                    <el-cascader
+                        @change="search"
+                        :options="optionsArr"
+                        v-model="searchform.classifynameArr"
+                        clearable
+                        placeholder="类型筛选"
+                        size="medium"
+                    />
+                </el-form-item>
+                <el-form-item label="">
+                    <el-select
+                        v-model.number="searchform.publishState"
+                        placeholder="发布筛选"
+                        size="medium"
+                        clearable
+                        style="width: 140px"
+                        @change="search"
+                    >
+                        <el-option label="已发布" :value="2"></el-option>
+                        <el-option label="未发布" :value="1"></el-option>
+                    </el-select>
+                </el-form-item>
+                <!-- <el-form-item label="">
+                    <el-select
+                        v-permission="permissionBtn.smartReportManageBtn.reportManage_reportList_sendTime"
+                        @change="search"
+                        v-model.number="searchform.msgIsSend"
+                        placeholder="推送消息状态"
+                        size="medium"
+                        clearable
+                        style="width: 140px"
+                    >
+                        <el-option label="未推送消息" :value="1"></el-option>
+                        <el-option label="已推送消息" :value="2"></el-option>
+                    </el-select>
+                </el-form-item> -->
+                <el-form-item>
+                    <el-input
+                        @input="search"
+                        placeholder="标题 / 创建人 / 更新人"
+                        v-model="searchform.key_word"
+                        clearable
+                        size="medium"
+                    >
+                        <i class="el-icon-search" slot="prefix" @click="search"></i>
+                    </el-input>
+                </el-form-item>
+                </el-form>
+            </div>
+            <template>
+                <el-table
+                    ref="table"
+                    :key="tableKey"
+                    :data="tableData"
+                    v-loading="listLoading"
+                    @sort-change="sortChange"
+                    element-loading-text="数据加载中..."
+                    border
+                    style="width: 100%"
+                >
+                <el-table-column
+                    prop="Title"
+                    label="报告标题"
+                    align="center"
+                    min-width="140"
+                >
+                    <template slot-scope="scope">
+                    <div @click="lookreportdtl(scope.row)">
+                        <span style="cursor: pointer; color: #4099ef" >{{ scope.row.Title }}</span>
+                        <span
+                            style="cursor: pointer; color: #4099ef"
+                            v-if="scope.row.MsgSendTime"
+                        >({{ scope.row.MsgSendTime.substring(5, 7)}}{{ scope.row.MsgSendTime.substring(8, 10) }})</span>
+                        <span
+                        style="cursor: pointer; color: #4099ef"
+                        v-else-if="scope.row.PublishTime"
+                        >({{ scope.row.PublishTime.substring(5, 7)
+                        }}{{ scope.row.PublishTime.substring(8, 10) }})</span
+                        >
+                        <span
+                        style="cursor: pointer; color: #4099ef"
+                        v-else-if="scope.row.CreateTime"
+                        >({{ scope.row.CreateTime.substring(5, 7)
+                        }}{{ scope.row.CreateTime.substring(8, 10) }})</span
+                        >
+
+                    </div>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    prop="Abstract"
+                    label="摘要"
+                    align="center"
+                    min-width="140"
+                ></el-table-column>
+                <el-table-column label="报告类型" align="center" min-width="140">
+                    <template slot-scope="scope"
+                    >{{ scope.row.ClassifyNameFirst }}
+                    <span v-if="scope.row.ClassifyNameSecond"
+                        >/ {{ scope.row.ClassifyNameSecond }}</span
+                    ></template
+                    >
+                </el-table-column>
+                <el-table-column
+                    prop="AdminRealName"
+                    label="创建人"
+                    align="center"
+                    min-width="100"
+                ></el-table-column>
+                <el-table-column
+                    prop="LastModifyAdminName"
+                    label="更新人"
+                    align="center"
+                    min-width="100"
+                ></el-table-column>
+                <el-table-column label="发布状态" align="center">
+                    <template slot-scope="scope">
+                    <span v-if="scope.row.State == '2'" style="color: #46c371"
+                        >已发布</span
+                    >
+                    <span v-if="scope.row.State == '1'">未发布</span>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    prop="PublishTime"
+                    label="发布时间"
+                    min-width="124"
+                    align="center"
+                    :formatter="formatterColumn"
+                >
+                    <template slot-scope="scope">
+                    <span>{{scope.row.PrePublishTime?scope.row.PrePublishTime:scope.row.PublishTime|formatTime}}</span>
+                    <svg style="position: relative;top:2px" v-if="scope.row.PrePublishTime&&scope.row.State == '1'" width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+                        <path d="M15.2002 8C15.2002 4.13401 12.0662 1 8.20019 1C4.3342 1 1.20019 4.13401 1.2002 8C1.2002 11.866 4.3342 15 8.2002 15C12.0662 15 15.2002 11.866 15.2002 8ZM5.2002 10L5.2002 9L8.17491 9L11.2002 4.5L11.9073 5.20711L8.58912 10L5.2002 10Z" fill="#0052D9"/>
+                    </svg>
+                    </template>
+                </el-table-column>
+                <!-- <el-table-column v-if="permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_reportList_sendTime)"
+                    prop="MsgSendTime"
+                    label="报告推送时间"
+                    min-width="124"
+                    align="center"
+                >
+                    <template slot-scope="scope">{{
+                    scope.row.MsgSendTime | formatTime
+                    }}</template>
+                </el-table-column> -->
+                <el-table-column
+                    prop="ContentModifyTime"
+                    label="更新时间"
+                    min-width="124"
+                    align="center"
+                    :formatter="formatterColumn"
+                ></el-table-column>
+                <el-table-column label="期数" align="center">
+                    <template slot-scope="scope">第{{ scope.row.Stage }}期</template>
+                </el-table-column>
+                <el-table-column label="频度" align="center">
+                    <template slot-scope="scope">{{ scope.row.Frequency }}</template>
+                </el-table-column>
+                <el-table-column
+                    :label="hasUV?'PV / UV':'PV'"
+                    align="center"
+                    :render-header="renderHeader"
+                    width="140"
+                >
+                    <template slot-scope="scope"
+                    >{{ hasUV? scope.row.Pv+'/'+scope.row.Uv:scope.row.Pv }}</template
+                    >
+                </el-table-column>
+                <el-table-column label="操作" align="center" min-width="130">
+                    <template slot-scope="scope">
+                    <div class="opt-btns">
+                        <template
+                        v-if="scope.row.State == 1"
+                        >
+                        <span
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_publish"
+                            class="editsty"
+                            @click="handlePublishReportOpt(scope.row)"
+                            v-if="scope.row.CanEdit"
+                            >发布</span
+                        >
+                        <span
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_reportEdit"
+                            class="editsty"
+                            @click="editreport(scope.row, 'edit')"
+                            v-if="scope.row.CanEdit"
+                            >编辑</span
+                        >
+                        <span
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_reportEdit"
+                            class="editsty"
+                            @click="editreport(scope.row, 'editing')"
+                            v-else
+                            >{{ scope.row.Editor || "" }}编辑中...</span
+                        >
+                        <span
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_reportDel"
+                            class="deletesty"
+                            @click="handleDelReport(scope.row)"
+                            v-if="scope.row.CanEdit"
+                            >删除</span
+                        >
+                        </template>
+                        <template v-if="scope.row.State == 2">
+                        <span v-if="permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_cancelPublish)"
+                            @click="handleReportPublishCancel(scope.row)"
+                            style="color: red; cursor: pointer"
+                            >取消发布</span>
+                        <!-- <span
+                            v-if="scope.row.MsgIsSend == 0 && permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_sendMsg)"
+                            style="color: #4099ef; cursor: pointer"
+                            @click="handleSendMsg(scope.row)"
+                            >推送消息</span
+                        >
+                        <span v-else-if="scope.row.MsgIsSend != 0&&permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_sendMsg)"
+                            style="color: red">已推送消息</span> -->
+                        </template>
+                        <span
+                        style="color: #46c371; cursor: pointer;display:inline-block"
+                        v-if="permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_audioUpload)"
+                        @click="openupload(scope.row.SmartReportId)"
+                        >音频上传</span
+                        >
+                        <span 
+                            style="cursor: pointer; color: #4099ef;display:inline-block" 
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_audioDownload"
+                            v-if="scope.row.VideoUrl"
+                            @click="handleDownloadVoice(scope.row)"
+                        >
+                            音频下载
+                        </span>
+                        <!-- <a
+                        v-permission="permissionBtn.smartReportManageBtn.reportManage_audioDownload"
+                        :href="scope.row.VideoUrl"
+                        v-if="scope.row.VideoUrl"
+                        :download="scope.row.VideoName"
+                        style="cursor: pointer; color: #4099ef"
+                        >音频下载</a
+                        > -->
+                    </div>
+                    </template>
+                </el-table-column>
+                </el-table>
+            </template>
+            <el-col :span="24" class="toolbar">
+                <el-pagination
+                    v-if="ispage"
+                    layout="total,prev,pager,next,jumper"
+                    background
+                    @current-change="handleCurrentChange"
+                    :page-size="pageSize"
+                    @size-change="handleSizeChange"
+                    :total="total"
+                    style="float: right"
+                />
+            </el-col>
+        </el-card>
+        <!-- 上传音频弹框 -->
+        <el-dialog
+            title="上传音频"
+            :visible.sync="uploadDialog"
+            :modal-append-to-body="false"
+            :close-on-click-modal="false"
+            :center="true"
+            v-dialogDrag
+            custom-class="dialogclass"
+            width="510px"
+        >
+            <div slot="title" style="display: flex; align-items: center">
+                <img
+                :src="$icons.up"
+                style="color: #fff; width: 16px; height: 16px; margin-right: 5px"
+                />
+                <span style="font-size: 16px">上传音频</span>
+            </div>
+            <el-form
+                :model="uploadForm"
+                ref="uploadForm"
+                label-position="right"
+                label-width="0px"
+                @submit.native.prevent
+            >
+                <el-form-item label="">
+                <input
+                    type="file"
+                    name="file"
+                    @change="fileSelected()"
+                    id="file"
+                    class="true-file"
+                    style="display: none"
+                />
+                <el-input
+                    type="text"
+                    v-model="uploadForm.audioname"
+                    readonly
+                    placeholder="上传报告录音"
+                    size="medium"
+                    style="width: 350px"
+                ></el-input>
+                <el-button type="primary" size="medium" @click.native="clickinput"
+                    >选择文件</el-button
+                >
+                </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer" style="text-align: center">
+                <el-button
+                type="primary"
+                plain
+                size="medium"
+                @click.native="uploadDialog = false"
+                >取 消</el-button
+                >
+                <el-button
+                type="primary"
+                size="medium"
+                :loading="uploadloading"
+                @click.native="uploadaudio"
+                >{{ uploadloading ? "上传中" : "上 传" }}</el-button
+                >
+            </div>
+        </el-dialog>
+
+        <!-- 新增报告基础信息 -->
+        <BaseInfo v-model="showAddReport" :id="0" />
+
+        <!-- 发布弹窗(改掉了不用这个弹窗了但是先留着吧) -->
+        <el-dialog
+            title="发布提示"
+            :visible.sync="showPublish"
+            :modal-append-to-body="false"
+            :close-on-click-modal="false"
+            :center="true"
+            v-dialogDrag
+            custom-class="dialogclass"
+            width="510px"
+            @close="handleClosePublish"
+        >
+            <div>
+                <div style="height: 100px; padding-top: 40px">
+                    <i
+                        class="el-icon-warning"
+                        style="font-size: 24px; color: #e6a23c; vertical-align: middle"
+                    ></i>
+                    {{isDSFB?'该报告已设置定时发布,是否立即发布报告?':'是否立即发布报告?'}}
+                </div>
+                <div style="margin-bottom: 20px; text-align: center">
+                    <el-button
+                        type="primary"
+                        plain
+                        style="width: 100px"
+                        @click="handleClosePublish"
+                        >取消</el-button
+                    >
+                    <el-button
+                        type="primary"
+                        style="width: 100px; margin: 0 20px"
+                        @click="handleConfirmPublishReport"
+                        >发布</el-button
+                    >
+                </div>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import { voiceupload } from "api/api.js";
+import {apiSmartReport}  from '@/api/modules/smartReport'
+import BaseInfo from './components/BaseInfo.vue'
+export default {
+    components:{BaseInfo},
+    computed:{
+        Role() {
+            let role = localStorage.getItem("Role") || "";
+            return role;
+        },
+        //是否有UV
+        hasUV(){
+            return this.permissionBtn.checkPermissionBtn(this.permissionBtn.smartReportManageBtn.reportManage_reportList_uv)
+        }
+    },
+    data() {
+        return {
+            hostapi: process.env.VUE_APP_API_ROOT + "/voice/download",
+            searchform: {
+                timeType:'publish_time',
+                dateValue: "",
+                frequency: "",
+                classifynameArr: "",
+                state: "",
+                key_word: "",
+                publish_sort: "desc",
+                msgIsSend: "",
+                publishState:'',
+            },
+            tableKey:0,
+            tableData:[],
+            PageIndex: 1,
+            total: 0,
+            pageSize: 15,
+            ispage: true,
+            listLoading: false,
+            optionsArr: [],//分类数据
+
+            uploadDialog: false,
+            uploadForm: {
+                id: "",
+                formdata: null,
+                audioname: "",
+            },
+            uploadloading: false,
+
+            showAddReport:false,
+
+            showPublish:false,
+            activeReportId:0,
+            isDSFB:false
+        }
+    },
+    methods:{
+        // 删除某个报告
+        handleDelReport(item){
+            //删除
+            this.$confirm("确认删除吗?", "提示", {
+                type: "warning",
+            }).then(() => {
+                apiSmartReport.delReport({ SmartReportId: item.SmartReportId }).then((res) => {
+                    if (res.Ret === 200) {
+                        this.$message.success(res.Msg);
+                        this.getReportList();
+                    }
+                });
+            }).catch(() => {});
+        },
+
+        //取消发布
+        handleReportPublishCancel(item){
+            apiSmartReport.publishReport({ SmartReportId: item.SmartReportId,PublishState:1 }).then((res) => {
+                if (res.Ret === 200) {
+                    this.$message.success(res.Msg);
+                    this.getReportList();
+                }
+            });
+        },
+
+        // 发布报告
+        handlePublishReportOpt(item){
+            this.activeReportId=item.SmartReportId
+            // this.isDSFB=item.PrePublishTime?true:false
+            // this.showPublish=true
+            this.$confirm(
+                item.PrePublishTime?'该报告已设置定时发布,是否立即发布报告?':'是否立即发布报告?', 
+                '发布提示', 
+                {
+                    confirmButtonText: '发布',
+                    cancelButtonText: '取消',
+                    type: 'warning',
+                    distinguishCancelAndClose:true,
+                    beforeClose:(action, instance,done)=>{
+                        if(action==='close') {
+                            //右上角
+                        } else if(action==='cancel') {
+                            //cancelButton
+                                
+                        }else {
+                            //confirmButton
+                            this.handleConfirmPublishReport()
+                        }
+                        done()
+                    }
+                }
+            )
+        },
+        handleConfirmPublishReport(){
+            apiSmartReport.publishReport({ SmartReportId: this.activeReportId,PublishState:2 }).then((res) => {
+                if (res.Ret === 200) {
+                    this.$message.success(res.Msg);
+                    this.getReportList();
+                    this.handleClosePublish()
+                }
+            });
+        },
+        handleClosePublish(){
+            this.activeReportId=0
+            this.isDSFB=false
+            this.showPublish=false
+        },
+
+        //编辑报告
+        async editreport(item, type){
+            //编辑前标记一下
+            const res = await apiSmartReport.markReport({
+                Status: 1,
+                SmartReportId: item.SmartReportId,
+            });
+            if (res.Ret === 200) {
+                if (res.Data.Status == 1) {
+                    this.$message.warning(res.Data.Msg || "该研报正在编辑,不可重复编辑");
+                    item.CanEdit = false;
+                    item.Editor = res.Data.Editor || "";
+                    return;
+                } else if (res.Data.Status == 0) {
+                    item.CanEdit = true;
+                    item.Editor = res.Data.Editor || "";
+                }
+            } else {
+                this.$message.error(res.ErrMsg || "未知错误,请稍后重试");
+                return;
+            }
+            let { href } = this.$router.resolve({
+                path: "/smartReportEdit",
+                query: { id: item.SmartReportId },
+            });
+            window.open(href, "_blank");
+        },
+
+        // 推送消息
+        // async handleSendMsg(item){
+        //     const res = await apiSmartReport.reportMsgSend({
+        //         SmartReportId: [item.Id],
+        //     });
+        //     if (res.Ret == 200) {
+        //         item.ThsMsgIsSend = 1;
+        //         this.$message.success("发送成功");
+        //     }
+        // },
+
+        // 跳转报告详情
+        lookreportdtl(item){
+            //如果没有预览权限,就不跳转
+            if(!this.permissionBtn.checkPermissionBtn(this.permissionBtn.smartReportManageBtn.reportManage_reportView)) return
+            
+            let { href } = this.$router.resolve({ 
+                path: '/smartReportDetail',
+                query:{
+                    id:item.SmartReportId,
+                    code:item.ReportCode
+                }
+            });
+            window.open(href, "_blank");
+        },
+
+        // 报告列表
+        async getReportList(){
+            let params = {
+                CurrentIndex: this.PageIndex,
+                PageSize: this.pageSize,
+                Frequency: this.searchform.frequency,
+                ClassifyIdFirst: this.searchform.classifynameArr[0]?this.searchform.classifynameArr[0]:'',
+                ClassifyIdSecond:this.searchform.classifynameArr[1]?this.searchform.classifynameArr[1]:'',
+                Keyword: this.searchform.key_word,
+                State:this.searchform.publishState,
+                TimeType:this.searchform.timeType,
+            };
+            if (this.searchform.dateValue) {
+                params.StartDate = this.searchform.dateValue[0];
+                params.EndDate = this.searchform.dateValue[1];
+            }
+            this.listLoading = true;
+            const res=await apiSmartReport.reportList(params)
+            if (res.Ret === 200) {
+                this.tableData = res.Data.List || [];
+                this.tableKey++
+                this.total = parseInt(res.Data.Paging.Totals);
+            }
+            this.listLoading = false;
+        },
+
+        //分页页码跳转
+        handleCurrentChange(current) {
+            this.PageIndex = current;
+            this.getReportList();
+        },
+
+        //搜索
+        search() {
+            this.ispage = false;
+            this.$nextTick(() => {
+                this.PageIndex = 1;
+                this.getReportList();
+                this.ispage = true;
+            });
+        },
+
+        //分页页码改变
+        handleSizeChange(val) {
+            this.pageSize = val;
+            this.getlist();
+        },
+
+        renderHeader(h, { column, $index }) {
+            //table表头自定义
+            return h("div", { attrs: { style: "padding:0;" } }, [
+                h("span", column.label),
+                h("el-tooltip", { props: { placement: "top" } }, [
+                h(
+                    "p",
+                    {
+                    slot: "content",
+                    attrs: { style: "display:block;padding:5px 0;width:420px;" },
+                    },
+                    "pv:报告被打开的次数,每次打开都计算一次(只统计有权限用户)"
+                ),
+                this.hasUV?h(
+                    "p",
+                    {
+                    slot: "content",
+                    attrs: { style: "display:block;padding:5px 0;width:420px;" },
+                    },
+                    "uv:访问报告的人数,每篇报告同一个人访问只计算一次(只统计有权限用户)"
+                ):h(''),
+                h(
+                    "el-button",
+                    {
+                    props: { icon: "el-icon-info" },
+                    attrs: { style: "border:none;background:none" },
+                    },
+                    ""
+                ),
+                ]),
+            ]);
+        },
+
+        //报告类型
+        getclassifylist() {
+            //获取分类列表
+            let params = { CurrentIndex: 0, PageSize: 1000, KeyWord: "",HideDayWeek:1,/*不显示晨报/周报*/ };
+            apiSmartReport.classifyList(params).then((res) => {
+                if (res.Ret == 200 && Array.isArray(res.Data.List)) {
+                    this.optionsArr = [];
+                    res.Data.List.forEach((item, index) => {
+                        let newitem = {
+                            label: item.ClassifyName,
+                            value: item.Id,
+                        };
+                        if (item.Child) {
+                            let childnode = [];
+                            item.Child.forEach((itemchild, i) => {
+                                childnode.push({
+                                    label: itemchild.ClassifyName,
+                                    value: itemchild.Id,
+                                });
+                            });
+                            newitem.children = childnode;
+                        }
+                        this.optionsArr.push(newitem);
+                    });
+                }
+            });
+        },
+
+        //唤醒上传弹窗
+        openupload(id) {
+            this.uploadForm = { id: id, formdata: null, audioname: "" };
+            this.uploadDialog = true;
+        },
+        //上传模拟点击
+        clickinput() {
+            $("#file").click();
+        },
+        //选择文件上传
+        fileSelected() {
+            const that = this;
+            if (document.getElementById("file").files[0]) {
+                let hostfile = document.getElementById("file").files[0];
+                let size = Math.floor(hostfile.size / 1024 / 1024);
+                if (size > 200) {
+                    that.$message.error("上传文件大小不能大于200M!");
+                    hostfile = {};
+                    return false;
+                }
+                if (
+                    hostfile.name.includes(".mp3") ||
+                    hostfile.name.includes(".wav") ||
+                    hostfile.name.includes(".wma")
+                ) {
+                let form = new FormData();
+                form.append("file", hostfile); //hostfile.name
+                form.append("SmartReportId", this.uploadForm.id);
+                    this.uploadForm.formdata = form;
+                    this.uploadForm.audioname = hostfile.name;
+                } else {
+                    that.$message.error("上传文件格式不正确!");
+                }
+            }
+        },
+        uploadaudio() {
+            if (this.uploadForm.formdata == null) {
+                this.$message.error("请选择文件");
+                return false;
+            }
+            this.uploadloading = true;
+            apiSmartReport.voiceupload(this.uploadForm.formdata).then((res) => {
+                if (res.Ret === 200) {
+                    this.$message.success("上传成功");
+                    this.getReportList();
+                }
+                this.uploadloading = false;
+                $("#file").val("");
+                this.uploadDialog = false;
+            });
+        },
+        // 下载音频
+        handleDownloadVoice(e){
+            const x = new window.XMLHttpRequest();
+            x.open('GET', e.VideoUrl, true);
+            x.responseType = 'blob';
+            x.onload = () => {
+                const url = window.URL.createObjectURL(x.response);
+                const a = document.createElement('a');
+                a.download = e.VideoName+'.mp3';
+                a.style.display = 'none'
+
+                let blob=new Blob([x.response])
+                a.href = URL.createObjectURL(blob);
+                a.target = '_blank'
+                document.body.append(a)
+                a.click();
+            };
+            x.send();
+        }
+    },
+
+    created() {
+        this.getclassifylist()
+        this.getReportList()
+    },
+}
+</script>
+
+<style>
+
+</style>

+ 33 - 6
src/views/system_manage/newAuthManage.vue

@@ -42,6 +42,7 @@ export default {
             loading: null, //loading
             isLook: false, //是否仅查看
             Role:{},//角色
+            checkList:[],//角色应勾选的list
         };
     },
     watch: {
@@ -84,12 +85,17 @@ export default {
                     HalfChoiceList = []
                 } = res.Data
                 this.authList = List || []
-                this.defaultCheckedKeys = ChoiceList.filter((item) => !HalfChoiceList.some((halfItem) =>
-                    item === halfItem))
+                this.checkList = ChoiceList
+                /* this.defaultCheckedKeys = ChoiceList.filter((item) => !HalfChoiceList.some((halfItem) =>
+                    item === halfItem)) */
                 const type = this.isLook?'all':''
-                this.authList.forEach(l=>{
-                    this.formatTree(l,type)
+                this.$nextTick(()=>{
+                    this.authList.forEach(l=>{
+                        this.formatTree(l,type)
+                        })
+                    this.defaultCheckedKeys=this.checkList
                 })
+                
             })
         },
         // 保存
@@ -113,17 +119,38 @@ export default {
         },
         formatTree(data,type){
             if(/* data.MenuType===2|| */type==='all'){
-                //同时存一下ParentId
-                //监听check事件,如果是ParentId的check,就把子项从defaultCheckedKeys中删除
                 data.Disabled = true
             }
+            //非叶子节点递归
             if(data.Children && data.Children.length){
                 data.Children = data.Children.map(i=>{
                     return this.formatTree(i,type)
                 })
             }
+            if(!data.Children||data.Children&&data.Children.length===0){
+                //叶子节点向上检查MenuId
+                this.checkDataList(data)
+            }
             return data
         },
+        //根据MenuId找到对应节点
+        findTreeNode(MenuId){
+            return this.$refs.checkboxTree.getNode(MenuId)
+        },
+        checkDataList(data){
+            //获取data的MenuId 和 checkList对比 
+            //如果MenuId不在checkList里,检查data.ParentId在不在checkList里,若在,则从checkList里去除
+            if(!this.checkList.includes(data.MenuId)&&this.checkList.includes(data.ParentId)){
+                const index = this.checkList.indexOf(data.ParentId)
+                index!==-1&&this.checkList.splice(index,1)
+                console.log('应该去除的节点',data.ParentId)
+            }
+            //向上检查MenuId
+            const parentNode = this.findTreeNode(data.ParentId)
+            if(parentNode){
+                this.checkDataList(parentNode.data)
+            }
+        }
     },
     created() {
         if (this.$route.query.id) {

+ 1 - 0
src/vuex/modules/permissionButton.js

@@ -16,6 +16,7 @@ const permissionButtons = {
             return new Promise((resolve,reject)=>{
                 departInterence.getRoleBtnAuth().then(res=>{
                     const buttons = res.Data || []
+                    // console.log(buttons.find(it => it.ButtonCode=="etaTable:excel:save"),'buttons');
                     commit('SET_PERMISSION_BUTTONS',buttons)
                     
                     let trialUserPermisson = !!buttons.find(item => item.ButtonCode =="trialUserAction")