浏览代码

Merge branch 'eta1.6.3'

cxmo 9 月之前
父节点
当前提交
c9d8648b58
共有 30 个文件被更改,包括 3634 次插入87 次删除
  1. 10 0
      src/api/modules/chartApi.js
  2. 153 11
      src/api/modules/chartRelevanceApi.js
  3. 二进制
      src/assets/img/icons/formula-add.png
  4. 4 0
      src/lang/commonLang.js
  5. 73 2
      src/lang/modules/StatisticAnalysis/ChartRelevance.js
  6. 11 0
      src/routes/modules/chartRoutes.js
  7. 1 0
      src/utils/buttonConfig.js
  8. 54 14
      src/views/chartRelevance_manage/components/chartCard.vue
  9. 10 2
      src/views/chartRelevance_manage/components/explainText.js
  10. 111 0
      src/views/chartRelevance_manage/components/saveChartSetting.vue
  11. 44 8
      src/views/chartRelevance_manage/components/saveChartTobaseDia.vue
  12. 2 2
      src/views/chartRelevance_manage/components/saveEdbToBaseDia.vue
  13. 5 0
      src/views/chartRelevance_manage/components/selectTarget.vue
  14. 4 1
      src/views/chartRelevance_manage/css/index.scss
  15. 10 2
      src/views/chartRelevance_manage/mixins/classifyMixin.js
  16. 210 0
      src/views/chartRelevance_manage/relevance/components/batchSelectFormula.vue
  17. 257 0
      src/views/chartRelevance_manage/relevance/components/batchSelectTable.vue
  18. 38 0
      src/views/chartRelevance_manage/relevance/components/formMixin.js
  19. 137 0
      src/views/chartRelevance_manage/relevance/components/modifyClassifyDialog.vue
  20. 616 0
      src/views/chartRelevance_manage/relevance/components/multipleIndForm.vue
  21. 268 0
      src/views/chartRelevance_manage/relevance/components/singleIndForm.vue
  22. 294 23
      src/views/chartRelevance_manage/relevance/list.vue
  23. 1089 0
      src/views/chartRelevance_manage/relevance/relevanceChartEditorV2.vue
  24. 111 0
      src/views/chartRelevance_manage/relevance/utils/config.js
  25. 70 0
      src/views/chartRelevance_manage/relevance/utils/index.js
  26. 17 4
      src/views/dataEntry_manage/components/SaveChartOther.vue
  27. 2 2
      src/views/dataEntry_manage/mixins/chartPublic.js
  28. 16 4
      src/views/mychart_manage/components/chartDetailDia.vue
  29. 15 11
      src/views/ppt_manage/mixins/pptMixins.js
  30. 2 1
      src/views/ppt_manage/newVersion/utils/untils.js

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

@@ -411,10 +411,20 @@ const dataBaseInterface = {
 		return http.post('/datamanage/edb_info/batch/add',params)
 		return http.post('/datamanage/edb_info/batch/add',params)
 	},
 	},
 	// 批量计算指标时获取当前筛选条件下选择的指标
 	// 批量计算指标时获取当前筛选条件下选择的指标
+	/**
+	 * @param {Object} params
+	 * @param {Number} params.EdbInfoType 0-指标;1-预测指标
+	 * @returns 
+	 */
 	getBatchFilterAddEdbList:params=>{
 	getBatchFilterAddEdbList:params=>{
 		return http.get('/datamanage/edb_info/calculate/multi/choice',params)
 		return http.get('/datamanage/edb_info/calculate/multi/choice',params)
 	},
 	},
 	//批量计算指标时待选指标的搜索列表
 	//批量计算指标时待选指标的搜索列表
+	/**
+	 * @param {Object} params
+	 * @param {Number} params.EdbInfoType 0-指标;1-预测指标
+	 * @returns 
+	 */
 	getBatchAddEdbSearchList:params=>{
 	getBatchAddEdbSearchList:params=>{
 		return http.get('/datamanage/edb_info/calculate/multi/search',params)
 		return http.get('/datamanage/edb_info/calculate/multi/search',params)
 	},
 	},

+ 153 - 11
src/api/modules/chartRelevanceApi.js

@@ -3,11 +3,22 @@ import http from "@/api/http.js"
 export default{
 export default{
     /**
     /**
    * 获取分类
    * 获取分类
+   * IsShowMe 是否仅展示我的:true-是
+   * ParentId 父级ID,一级分类不传或者0即可
+   * Source 来源:3-相关性(不传默认也为这个);4-滚动相关性
    * @returns 
    * @returns 
    */
    */
   classifyList:  params =>{
   classifyList:  params =>{
     return http.get('/correlation/chart_classify/list',params)
     return http.get('/correlation/chart_classify/list',params)
   },
   },
+  /**
+   * 分类树
+   * @param {*} params 
+   * @returns 
+   */
+  classifyTree:params=>{
+    return http.get('/correlation/chart_classify/tree',params)
+  },
 
 
   /**
   /**
    * 新增分类
    * 新增分类
@@ -46,8 +57,14 @@ export default{
   },
   },
 
 
   /**
   /**
-   * 移动分类
-   * @param {*} params ClassifyId "PrevClassifyId":1, "NextClassifyId":2,
+   * 移动分类or图表
+   * @param {*} params "ClassifyId": 228, //分类ID
+    "ParentClassifyId": 0, //父级分类ID
+    "PrevClassifyId": 0, //上一个兄弟节点分类ID
+    "NextClassifyId": 227, //下一个兄弟节点分类ID
+    "ChartInfoId": 0, //图表ID,如果图表ID>0则移动对象为图表,否则默认为移动分类
+    "PrevChartInfoId": 0, //上一个兄弟节点图表ID
+    "NextChartInfoId": 0 //下一个兄弟节点图表ID
    * @returns 
    * @returns 
    */
    */
   classifyMove: params => {
   classifyMove: params => {
@@ -253,15 +270,140 @@ export default{
     return http.post('/datamanage/multiple_graph/preview_cure',params)
     return http.post('/datamanage/multiple_graph/preview_cure',params)
   },
   },
 
 
-  /**
-	 * 设置图表对应版本信息
-	 * @param {*} params 
-	 * ChartInfoId ChartName
-	 * @returns 
-	 */
-	setChartLangInfo: params => {
-		return http.post('/correlation/chart_info/base/edit',params)
-	},
+    /**
+     * 设置图表对应版本信息
+     * @param {*} params 
+     * ChartInfoId ChartName
+     * @returns 
+     */
+    setChartLangInfo: params => {
+        return http.post('/correlation/chart_info/base/edit',params)
+    },
+
+    /* 相关性功能拓展 */
+
+    /**
+     * 获取计算方式列表
+     * @param {Object} params 
+     * @param {Number} params.EdbInfoType  指标类型:0-普通指标;1-预测指标
+     * @returns 
+     */
+    getCalculateFormula:params => {
+        return http.get('/datamanage/factor_edb_series/calculate_func/list',params)
+    },
+    /**
+     * 添加多因子系列
+     * @param {Object} params 
+     * @param {String} params.SeriesName 系列名称
+     * @param {Number} params.EdbInfoType 指标类型:0-普通指标; 1-预测指标
+     * @param {Object[]} params.Calculates 选择的计算公式
+     * @param {String} params.Calculates[].Formula N值/移动天数/指数修匀alpha值/计算公式等
+     * @param {String} params.Calculates[].Calendar 公历/农历
+     * @param {String} params.Calculates[].Frequency 需要转换的频度
+     * @param {String} params.Calculates[].MoveType 移动方式:1-领先(默认);2-滞后
+     * @param {String} params.Calculates[].MoveFrequency 移动频度
+     * @param {String} params.Calculates[].FromFrequency 来源的频度
+     * @param {Number} params.Calculates[].Source 计算方式来源
+     * @param {Number} params.Calculates[].Sort 计算顺序
+     * @param {Number[]} params.EdbInfoIds 需要计算的指标
+     * @returns 
+     */
+    addFactorSeries:params=>{
+        return http.post('/datamanage/factor_edb_series/add',params)
+    },
+    /**
+     * 编辑多因子系列
+     * @param {Object} params 
+     * @param {String} params.SeriesId //因子系列ID
+     * //其他同添加
+     * @returns 
+     */
+    editFactorSeries:params=>{
+        return http.post('/datamanage/factor_edb_series/edit',params)
+    },
+    /**
+     * 获取多因子系列详情
+     * @param {Object} params
+     * @param {Number} params.SeriesId 因子指标系列ID
+     * @returns 
+     */
+    getFactorSeriesDetail:params=>{
+        return http.get('/datamanage/factor_edb_series/detail',params)
+    },
+    /**
+     * 获取相关性矩阵
+     * @param {Object} params
+     * @param {Number} params.BaseEdbInfoId 标的指标ID
+     * @param {Object} params.Correlation
+     * @param {Number} params.Correlation.CalculateValue 计算窗口
+     * @param {String} params.Correlation.CalculateUnit 计算窗口频度
+     * @param {Number} params.Correlation.LeadValue 分析周期
+     * @param {String} params.Correlation.LeadUnit 分析周期频度
+     * @param {Number[]} params.SeriesIds 因子系列Id
+     * @returns 
+     */
+    getCorrelationMatrix:params=>{
+        return http.post('datamanage/factor_edb_series/correlation/matrix',params)
+    },
+    /**
+     * 新增多因子相关性图表
+     * @param {Object} params 
+     * @param {String} params.ChartName
+     * @param {Number} params.ClassifyId
+     * @param {Number} params.AnalysisMode 分析模式:0-单因子;1-多因子
+     * @param {Number} params.BaseEdbInfoId 标的指标ID
+     * @param {Object} params.FactorCorrelation
+     * @param {Number} params.FactorCorrelation.CalculateValue 领先期数
+     * @param {String} params.FactorCorrelation.CalculateUnit 频度
+     * @param {Number} params.FactorCorrelation.LeadValue 计算窗口
+     * @param {String} params.FactorCorrelation.LeadUnit 计算频度
+     * @param {Object[]} params.FactorCorrelation.SeriesEdb 关联系列指标
+     * @param {Number} params.FactorCorrelation.SeriesEdb[].SeriesId
+     * @param {Number} params.FactorCorrelation.SeriesEdb[].EdbInfoId
+     * @param {Object} params.ExtraConfig
+     * @param {Object[]} params.ExtraConfig.LegendConfig 
+     * @param {String} params.ExtraConfig.LegendConfig.LegendName
+     * @param {Number} params.ExtraConfig.LegendConfig.SeriesId
+     * @param {Number} params.ExtraConfig.LegendConfig.EdbInfoId 
+     * @param {String} params.ExtraConfig.LegendConfig.Color 
+     * @param {Object} params.SourcesFrom
+     * @param {Boolean} params.SourcesFrom.isShow
+     * @param {String} params.SourcesFrom.text
+     * @param {String} params.SourcesFrom.color
+     * @param {Number} params.SourcesFrom.fontSize
+     * @returns 
+     */
+    addMultipleFactor:params=>{
+        return http.post('/correlation/chart_info/multi_factor/add',params)
+    },
+    /**
+     * 更新多因子相关性图表
+     * @param {*} params 
+     * @param {Number} params.ChartInfoId
+     * 其他同新增
+     * @returns 
+     */
+    editMultipleFactor:params=>{
+        return http.post('/correlation/chart_info/multi_factor/edit',params)
+    },
+    /**
+     * 多因子图表详情
+     * @param {Object} params 
+     * @param {String} params.UniqueCode
+     * @returns 
+     */
+    getMultipleChartDetail:params=>{
+        return http.get('/datamanage/chart_info/common/detail/from_unique_code',params)
+    },
+    /**
+     * 多因子表单及矩阵详情
+     * @param {*} params
+     * @param {Number} params.UniqueCode 
+     * @returns 
+     */
+    getMultipleFactorDetail:params=>{
+        return http.get('/correlation/chart_info/multi_factor/detail',params)
+    }
 
 
 }
 }
 
 

二进制
src/assets/img/icons/formula-add.png


+ 4 - 0
src/lang/commonLang.js

@@ -13,6 +13,10 @@ export default {
       en: "Confirm",
       en: "Confirm",
       zh: "确定",
       zh: "确定",
     },
     },
+    calculate_btn:{
+        en:'Calculate',
+        zh:'计算'
+    },
     confirm_save_btn: {
     confirm_save_btn: {
       en: "Save",
       en: "Save",
       zh: "保存 ",
       zh: "保存 ",

+ 73 - 2
src/lang/modules/StatisticAnalysis/ChartRelevance.js

@@ -34,7 +34,31 @@ export const ChartRelevanceEn = {
     update_edb:'update',
     update_edb:'update',
     update_success:'Update success',
     update_success:'Update success',
     chart_save_as:'Save Chart As',
     chart_save_as:'Save Chart As',
-    check_value_hint:'Correlation calculation window must be ≥2* analysis period'
+    check_value_hint:'Correlation calculation window must be ≥2* analysis period',
+
+    analytical_model:'Mode',
+    single_indicator:'Single Indicator',
+    multiple_indicators:'Multiple Indicators',
+    target_indicator:'Target Indicator',
+    factor_indicators:'Factor Indicators',
+    add_factor_indicators:'Add factor indicators',
+
+    multiple_table_head_01:'Correlation matrix with the Target Indicator',
+    multiple_table_head_02:'Original Indicator name',
+    multiple_table_head_03:'Number of leading periods({unit})', //$t('xxx',{unit:''})
+    multiple_table_btn_add:'Add Curve',
+    multiple_table_btn_del:'Del Curve',
+    chart_curve_add_hint:'Only 10 curves are supported',
+    infoform_rules_hint_01:'IndexA not selected',
+    infoform_rules_hint_02:'IndexB not selected',
+    infoform_rules_hint_03:'Target Index not selected',
+    infoform_rules_hint_04:'Calculation Window not filled in',
+    infoform_rules_hint_05:'Analysis Period not filled in',
+    formula_add_btn:'Add Formula',
+    series_name:'Index Series Name',
+    series_name_placeholder:'Input Index Series Name',
+    formulaform_add_hint:'Only 5 formulas are supported',
+    batch_select_hint:'Please Select Index',
 };
 };
   
   
 /* 中文 */
 /* 中文 */
@@ -70,7 +94,54 @@ export const ChartRelevanceZh = {
     update_success:'更新成功',
     update_success:'更新成功',
     chart_save_as:'图表另存为',
     chart_save_as:'图表另存为',
 
 
-    check_value_hint:'相关性计算窗口必须≥2*分析周期'
+    check_value_hint:'相关性计算窗口必须≥2*分析周期',
+
+    analytical_model:'分析模式',
+    single_indicator:'单因子',
+    multiple_indicators:'多因子',
+    target_indicator:'标的指标',
+    factor_indicators:'因子指标系列',
+    add_factor_indicators:'添加因子指标系列',
+    /**
+     * Chart.ChartType
+     * 曲线图:spline_name
+     * 相关性:correlation_name
+     * 滚动相关性:rolling_correlation_name
+     * Chart
+     * 请选择时间段:choose_time
+     * 
+     * 操作 Edb.Detail.e_opera
+     * 数据来源 Edb.Detail.source
+     * 暂无信息 Common.no_info_msg
+     * 加入已选指标 EtaBasePage.add_to_selections
+     * N不能为空 Edb.Valids.n_msg
+     * alpha值不能为空 Edb.Valids.alpha_msg
+     */
+    multiple_table_head_01:'与标的指标的相关性矩阵',
+    multiple_table_head_02:'原始指标名称',
+    multiple_table_head_03:'领先期数({unit})', //$t('xxx',{unit:''})
+    multiple_table_btn_add:'添加曲线',
+    multiple_table_btn_del:'删除曲线',
+    chart_curve_add_hint:'最多只支持添加10条曲线',
+    infoform_rules_hint_01:'指标A未选择',
+    infoform_rules_hint_02:'指标B未选择',
+    infoform_rules_hint_03:'标的指标未选择',
+    infoform_rules_hint_04:'计算窗口未填写',
+    infoform_rules_hint_05:'分析周期未填写',
+    formula_add_btn:'添加计算公式',
+    series_name:'指标系列名称',
+    series_name_placeholder:'请输入指标系列名称',
+    formulaform_add_hint:'最多仅能添加5个计算公式',
+    batch_select_hint:'请选择需要添加的指标',
+
+
+
+
+
+
+
+
+
 };
 };
   
   
 /**
 /**

+ 11 - 0
src/routes/modules/chartRoutes.js

@@ -381,6 +381,17 @@ export default [
 					pathName_en:"Correlation analysis"
 					pathName_en:"Correlation analysis"
 				}
 				}
 			},
 			},
+			{
+				path: 'relevancechartEditorV2',
+				name: '编辑图表',
+				component:()=>import('@/views/chartRelevance_manage/relevance/relevanceChartEditorV2.vue'),
+				meta: {
+					name_en:"edit Correlation analysis",
+					pathFrom: "chartrelevance",
+					pathName: "相关性图表",
+					pathName_en:"Correlation analysis"
+				}
+			},
 			{
 			{
 				path: 'statisticFeatureList',
 				path: 'statisticFeatureList',
 				name: '统计特征',
 				name: '统计特征',

+ 1 - 0
src/utils/buttonConfig.js

@@ -548,6 +548,7 @@ export const statisticPermission = {
     corrAnalysis_onlyMine:'corrAnalysis:onlyMine',//只看我的
     corrAnalysis_onlyMine:'corrAnalysis:onlyMine',//只看我的
     corrAnalysis_classifyOpt_edit:'corrAnalysis:classifyOpt:edit',//添加/编辑分类
     corrAnalysis_classifyOpt_edit:'corrAnalysis:classifyOpt:edit',//添加/编辑分类
     corrAnalysis_classifyOpt_delete:'corrAnalysis:classifyOpt:delete',//删除分类
     corrAnalysis_classifyOpt_delete:'corrAnalysis:classifyOpt:delete',//删除分类
+    corrAnalysis_classifyOpt_move:'corrAnalysis:classifyOpt:move',//移动分类
         /*---图表操作栏--- */
         /*---图表操作栏--- */
     corrAnalysis_del:'corrAnalysis:del',
     corrAnalysis_del:'corrAnalysis:del',
     corrAnalysis_enNameSetting:'corrAnalysis:enNameSetting',
     corrAnalysis_enNameSetting:'corrAnalysis:enNameSetting',

+ 54 - 14
src/views/chartRelevance_manage/components/chartCard.vue

@@ -18,6 +18,7 @@
               <el-dropdown-item :command="{entryType,scence:'saveOther'}">{{$t('StatisticAnalysis.ChartRelevance.save_other')}}</el-dropdown-item>
               <el-dropdown-item :command="{entryType,scence:'saveOther'}">{{$t('StatisticAnalysis.ChartRelevance.save_other')}}</el-dropdown-item>
             </el-dropdown-menu>
             </el-dropdown-menu>
           </el-dropdown>
           </el-dropdown>
+          <!-- 保存指标 -->
           <el-button type="primary" v-show="!isEdbAdd" @click="saveEdb(entryType)">{{$t('StatisticAnalysis.ChartRelevance.save_edb')}}</el-button>
           <el-button type="primary" v-show="!isEdbAdd" @click="saveEdb(entryType)">{{$t('StatisticAnalysis.ChartRelevance.save_edb')}}</el-button>
         </template>
         </template>
 
 
@@ -28,13 +29,17 @@
           @click="updateChartHandle(entryType)" 
           @click="updateChartHandle(entryType)" 
           @command="chartCommandHandle"
           @command="chartCommandHandle"
         >
         >
+          <!-- 更新图表/指标 -->
           {{$t('StatisticAnalysis.ChartRelevance.update_edb')}}
           {{$t('StatisticAnalysis.ChartRelevance.update_edb')}}
           <el-dropdown-menu slot="dropdown" v-show="isHaveSaveOtherHandle">
           <el-dropdown-menu slot="dropdown" v-show="isHaveSaveOtherHandle">
+            <!-- 另存为图表/指标 -->
             <el-dropdown-item :command="{entryType,scence:'saveOther'}">{{$t('Table.save_as')}}</el-dropdown-item>
             <el-dropdown-item :command="{entryType,scence:'saveOther'}">{{$t('Table.save_as')}}</el-dropdown-item>
           </el-dropdown-menu>
           </el-dropdown-menu>
         </el-dropdown>
         </el-dropdown>
+        <!-- 保存图表 -->
         <el-button type="primary" @click="saveChart(entryType)" v-show="!isChartAdd">{{$t('Dialog.confirm_save_btn')}}</el-button>
         <el-button type="primary" @click="saveChart(entryType)" v-show="!isChartAdd">{{$t('Dialog.confirm_save_btn')}}</el-button>
-
+        <!-- 图例设置 -->
+        <el-button type="primary" plain @click="handleChartSettiing" v-show="isChartSetting">图例设置</el-button>
       </div>
       </div>
     </div>
     </div>
     <div class="chartWrapper">
     <div class="chartWrapper">
@@ -46,7 +51,7 @@
         :options="options"
         :options="options"
         :index="String(entryType)"
         :index="String(entryType)"
         :ref="`chartRef${entryType}`"
         :ref="`chartRef${entryType}`"
-        height="350"
+        :height="height||350"
       />
       />
     </div>
     </div>
   </div>
   </div>
@@ -62,32 +67,42 @@ export default {
   mixins: [chartSetMixin],
   mixins: [chartSetMixin],
   computed: {
   computed: {
     isHaveButton() {
     isHaveButton() {
-      let routes = ['/relevancechartEditor','/statisticFeatureChartEditor']
+      let routes = ['/relevancechartEditor','/statisticFeatureChartEditor','/relevancechartEditorV2']
       return routes.includes(this.$route.path)
       return routes.includes(this.$route.path)
     },
     },
     cardTitle() {
     cardTitle() {
       let title='';
       let title='';
       if(this.$route.path==='/relevancechartEditor') {
       if(this.$route.path==='/relevancechartEditor') {
         title = [3,4].includes(this.entryType) ? `滚动相关性${this.entryType-2}` : this.entryType===1 ? this.chartInfo.ChartName : ''
         title = [3,4].includes(this.entryType) ? `滚动相关性${this.entryType-2}` : this.entryType===1 ? this.chartInfo.ChartName : ''
-      }else if(this.$route.path==='/statisticFeatureChartEditor') {
+      }else if(this.$route.path==='/relevancechartEditorV2'){
+        if(this.settings.Model===1){
+            title = [3,4].includes(this.entryType) ? `滚动相关性${this.entryType-2}` : this.entryType===1 ? this.chartInfo.ChartName : this.relevanceChartData.ChartName
+        }else{
+            title = this.relevanceChartData.ChartName
+        }
+      }
+      else if(this.$route.path==='/statisticFeatureChartEditor') {
         title = this.chartInfo.ChartName
         title = this.chartInfo.ChartName
       }
       }
       return title
       return title
     },
     },
     isHaveEdbHandle() { //有指标操作的区域
     isHaveEdbHandle() { //有指标操作的区域
       //相关性 3,4区域可保存指标 统计图 2,3区域
       //相关性 3,4区域可保存指标 统计图 2,3区域
-      return (([3,4].includes(this.entryType)&&this.$route.path==='/relevancechartEditor')
+      return (([3,4].includes(this.entryType)&&['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path))
         || ([2,3].includes(this.entryType)&&this.$route.path==='/statisticFeatureChartEditor'))
         || ([2,3].includes(this.entryType)&&this.$route.path==='/statisticFeatureChartEditor'))
     },
     },
     isHaveSaveOtherHandle() {//有图表另存的区域
     isHaveSaveOtherHandle() {//有图表另存的区域
         //相关性 1,2区域有另存 统计图 4个区域都有另存
         //相关性 1,2区域有另存 统计图 4个区域都有另存
-      return (([1,2].includes(this.entryType) && this.$route.path==='/relevancechartEditor')
+      return (([1,2].includes(this.entryType) && ['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path))
        || (this.$route.path==='/statisticFeatureChartEditor'))
        || (this.$route.path==='/statisticFeatureChartEditor'))
     }
     }
   },
   },
   watch: {
   watch: {
-    data(nval) {
-      this.setRenderChartData(nval)
+    data:{
+        handler(nval) {
+            this.setRenderChartData(nval)
+        },
+        deep:true
     }
     }
   },
   },
   props: {
   props: {
@@ -108,6 +123,13 @@ export default {
     },
     },
     settings: {
     settings: {
       type: Object
       type: Object
+    },
+    isChartSetting:{ //是否显示图表设置
+        type:Boolean,
+        default:false
+    },
+    height:{
+        type:String
     }
     }
 
 
   },
   },
@@ -132,6 +154,7 @@ export default {
         case 3: //3相关性 
         case 3: //3相关性 
           this.chartInfo = data.ChartInfo;
           this.chartInfo = data.ChartInfo;
           this.relevanceChartData={
           this.relevanceChartData={
+            ChartName:data.ChartInfo.ChartName,
             ChartInfo:data.ChartInfo,
             ChartInfo:data.ChartInfo,
             EdbInfoList:data.EdbInfoList,
             EdbInfoList:data.EdbInfoList,
             XEdbIdValue:data.XEdbIdValue || data.XDateTimeValue,
             XEdbIdValue:data.XEdbIdValue || data.XDateTimeValue,
@@ -139,12 +162,16 @@ export default {
             YDataList:[
             YDataList:[
               {
               {
                 Value:data.YDataList[0].Value,
                 Value:data.YDataList[0].Value,
-                Color:'#00f',
-                Name:data.ChartInfo.ChartName,
+                Color:data.YDataList[0].Color||'#00f',
+                Name:data.YDataList[0].Name||data.ChartInfo.ChartName,
                 NameEn:''
                 NameEn:''
               }
               }
             ]
             ]
           }
           }
+          if(this.settings.Model===2){
+            this.relevanceChartData.YDataList = data.YDataList
+            //this.relevanceChartData.ChartName = data.ChartInfo.EdbName+'相关性分析(xxx)'
+          }
           this.tableData=data.EdbInfoList||[]
           this.tableData=data.EdbInfoList||[]
           this.initRelevanceChartData()
           this.initRelevanceChartData()
           break
           break
@@ -172,6 +199,10 @@ export default {
           break
           break
       }
       }
     },
     },
+    handleChartSettiing(){
+        this.$emit('chartSettingChange',this.relevanceChartData)
+    },
+    
 
 
     /* 拟合方程曲线 */
     /* 拟合方程曲线 */
     initFittingEquation(data) {
     initFittingEquation(data) {
@@ -239,8 +270,17 @@ export default {
         4: 10
         4: 10
       }
       }
       let Source;
       let Source;
-      if(this.$route.path==='/relevancechartEditor') {
+      if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)) {
         Source = entryType;
         Source = entryType;
+        //多因子参数不一样,这里直接返回
+        if(this.$parent.chartInfo.Model===2){
+            this.$emit('handleEdit',{
+                ChartName:this.chartInfo.ChartName,
+                ClassifyId:this.chartInfo.ClassifyId,
+                type:'edit'
+            })
+            return
+        }
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
         Source = sourceMap[entryType]
         Source = sourceMap[entryType]
       }
       }
@@ -251,7 +291,7 @@ export default {
         ...this.settings
         ...this.settings
       }
       }
       let res = null;
       let res = null;
-      if(this.$route.path==='/relevancechartEditor') {
+      if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)) {
         res = await chartRelevanceApi.saveChart(params)
         res = await chartRelevanceApi.saveChart(params)
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
         res = await statisticFeatureInterface.saveChart(params);
         res = await statisticFeatureInterface.saveChart(params);
@@ -273,7 +313,7 @@ export default {
         4: 10
         4: 10
       }
       }
       let Source;
       let Source;
-      if(this.$route.path==='/relevancechartEditor') {
+      if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)) {
         Source = entryType;
         Source = entryType;
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
         Source = sourceMap[entryType]
         Source = sourceMap[entryType]
@@ -286,7 +326,7 @@ export default {
 			}
 			}
       
       
       let res = null;
       let res = null;
-      if(this.$route.path==='/relevancechartEditor') {
+      if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)) {
         res = await chartRelevanceApi.saveEdb(params)
         res = await chartRelevanceApi.saveEdb(params)
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
         res = await statisticFeatureInterface.saveEdb(params);
         res = await statisticFeatureInterface.saveEdb(params);

+ 10 - 2
src/views/chartRelevance_manage/components/explainText.js

@@ -1,6 +1,7 @@
 //相关性分析
 //相关性分析
 export const chartrelevanceTextArr = [
 export const chartrelevanceTextArr = [
     `<p style='font-weight:bold;'>相关性计算处理逻辑:</p>
     `<p style='font-weight:bold;'>相关性计算处理逻辑:</p>
+    <p style='font-weight:bold;'>单因子:</p>
     <p>1、取数:取计算窗口的时间长度,从当前时间往前推移对应的时间长度,取该日期区间,指标A序列值和指标B序列值;</p>
     <p>1、取数:取计算窗口的时间长度,从当前时间往前推移对应的时间长度,取该日期区间,指标A序列值和指标B序列值;</p>
     <p>2、变频:根据指标A和指标B的频度对取出的数据序列做如下处理</p>
     <p>2、变频:根据指标A和指标B的频度对取出的数据序列做如下处理</p>
     <p>①指标A高频,对指标B升频(线性方程插值法补全数据);</p>
     <p>①指标A高频,对指标B升频(线性方程插值法补全数据);</p>
@@ -14,10 +15,14 @@ export const chartrelevanceTextArr = [
     <p>2、分析周期:指标B领先A的期数,如配置参数10个月,表示B领先A -10月、B领先A -9月,...,B领先A 9月、B领先A 10月,每期分别计算相关性值;</p>`,
     <p>2、分析周期:指标B领先A的期数,如配置参数10个月,表示B领先A -10月、B领先A -9月,...,B领先A 9月、B领先A 10月,每期分别计算相关性值;</p>`,
     `<p style='font-weight:bold;'>滚动相关性配置:</p>
     `<p style='font-weight:bold;'>滚动相关性配置:</p>
     <p>1、计算窗口:参与计算的时间段长度,从两个指标都有值的日期开始滚动的取计算窗口长度的值进行计算,如配置计算窗口1个月,则2023.7.28的值取2023.6.28~2023.7.28时间段,2023.7.27的值取2023.6.27~2023.7.27时间段;</p>
     <p>1、计算窗口:参与计算的时间段长度,从两个指标都有值的日期开始滚动的取计算窗口长度的值进行计算,如配置计算窗口1个月,则2023.7.28的值取2023.6.28~2023.7.28时间段,2023.7.27的值取2023.6.27~2023.7.27时间段;</p>
-    <p>2、B领先A:B指标领先A指标的参数,为0时不领先;</p>`
+    <p>2、B领先A:B指标领先A指标的参数,为0时不领先;</p>
+    <p style='height:20px;'></p>
+    <p style='font-weight:bold;'>多因子:</p>
+    <p>可一次选择多个指标,与标的指标进行相关性计算。每一个因子指标与标的指标的计算逻辑,同单因子模式。</p>`
 ]
 ]
 export const chartrelevanceTextArrEn=[
 export const chartrelevanceTextArrEn=[
     `<p style='font-weight:bold;'>Instruction for Correlation Calculation Processing Logic:</p>
     `<p style='font-weight:bold;'>Instruction for Correlation Calculation Processing Logic:</p>
+    <p style='font-weight:bold;'>Single Indicator:</p>
     <p>1、 Data Retrieval: Determine the time length of the calculation window, and move backwards from the current time by this duration to obtain a range of dates. Retrieve values for indicator A series and indicator B series within this date range.</p>
     <p>1、 Data Retrieval: Determine the time length of the calculation window, and move backwards from the current time by this duration to obtain a range of dates. Retrieve values for indicator A series and indicator B series within this date range.</p>
     <p>2、Frequency Adjustment:</p>
     <p>2、Frequency Adjustment:</p>
     <p>a. If indicator A is higher frequency, upsample indicator B (use linear equation interpolation to fill in data).</p>
     <p>a. If indicator A is higher frequency, upsample indicator B (use linear equation interpolation to fill in data).</p>
@@ -31,7 +36,10 @@ export const chartrelevanceTextArrEn=[
     <p>2. Analysis Period: The number of periods that indicator B leads ahead of A; for instance, if configured with a parameter of 10 months, it indicates that B leads A by -10 months to +10 months, with correlation values calculated separately for each period.</p>`,
     <p>2. Analysis Period: The number of periods that indicator B leads ahead of A; for instance, if configured with a parameter of 10 months, it indicates that B leads A by -10 months to +10 months, with correlation values calculated separately for each period.</p>`,
     `<p style='font-weight:bold;'>Rolling Correlation Configuration:</p>
     `<p style='font-weight:bold;'>Rolling Correlation Configuration:</p>
     <p>1. Calculation Window: The length of time involved in calculations starts from when both indicators have available values and rolls forward taking values within the duration of the calculation window for computation. For example, if configured with a 1-month calculation window, then the value for July 28th, 2023 will be taken from June 28th to July 28th, 2023; while the value for July 27th will be taken from June 27th to July 27th.</p>
     <p>1. Calculation Window: The length of time involved in calculations starts from when both indicators have available values and rolls forward taking values within the duration of the calculation window for computation. For example, if configured with a 1-month calculation window, then the value for July 28th, 2023 will be taken from June 28th to July 28th, 2023; while the value for July 27th will be taken from June 27th to July 27th.</p>
-    <p>2. B Leads A: The parameter by which indicator B leads ahead of indicator A; when set to 0, there is no lead.</p>`
+    <p>2. B Leads A: The parameter by which indicator B leads ahead of indicator A; when set to 0, there is no lead.</p>
+    <p style='height:20px;'></p>
+    <p style='font-weight:bold;'>Multiple Indicators:</p>
+    <p>Multiple indicators can be selected at a time to calculate the correlation with the target indicator. The calculation logic of each factor index and the target index is the same as the Single Indicato.</p>`
 ]
 ]
 
 
 //拟合方程曲线
 //拟合方程曲线

+ 111 - 0
src/views/chartRelevance_manage/components/saveChartSetting.vue

@@ -0,0 +1,111 @@
+<template>
+    <el-dialog 
+        :visible.sync="isSettingChartShow"
+        :close-on-click-modal="false"
+        :modal-append-to-body='false'
+        @close="$emit('close')"
+        center width="500px"
+        title="图例设置"
+        custom-class="chart-setting-dialog"
+    >
+        <div class="dialog-content">
+            <el-form 
+                :modal="settingData" 
+                :rules="formRules"
+                label-width="100px"
+                ref="settingForm">
+                <el-form-item label="图表名称">
+                    <el-input v-model="settingData.chartName"></el-input>
+                </el-form-item>
+                <el-form-item label="图例名称" style="margin-bottom: 0;">
+                </el-form-item>
+                <el-form-item label-width="0px">
+                    <div class="option-box">
+                        <div class="option-item" v-for="(data,index) in settingData.YDataList" :key="index">
+                            <el-color-picker
+                                v-model="data.Color"
+                                size="mini"
+                                show-alpha
+                                :predefine="predefineColors"/>
+                            <el-input v-model="data.Name"></el-input>
+                        </div>
+                    </div>
+                </el-form-item>
+                <el-form-item label="数据来源">
+                    <el-input v-model="settingData.SourcesFrom.text"></el-input>
+                    <el-switch v-model="settingData.SourcesFrom.isShow"></el-switch>
+                </el-form-item>
+            </el-form>
+        </div>
+        <div class="dialog-footer">
+            <el-button type="primary" plain @click="$emit('close')">{{$t('Dialog.cancel_btn')}}</el-button>
+            <el-button type="primary" @click="handleSaveChartSetting">{{$t('Dialog.confirm_btn')}}</el-button>
+        </div>
+
+    </el-dialog>
+</template>
+
+<script>
+import { defaultOpts } from '@/utils/defaultOptions';
+export default {
+    props:{
+        isSettingChartShow:{
+            type:Boolean
+        },
+        settingData:{
+            type:Object,
+        }
+    },
+    data() {
+        return {
+            predefineColors: defaultOpts.colors.slice(0, 2),
+            formRules:{},
+        };
+    },
+    methods: {
+        handleSaveChartSetting(){
+            this.$emit('saveChartSetting')
+        },
+
+    },
+};
+</script>
+
+<style lang="scss">
+.chart-setting-dialog{
+    .el-input{
+        width:230px;
+    }
+    .el-color-picker--mini .el-color-picker__trigger {
+        width: 60px;
+        height: 25px;
+        padding: 0;
+        margin:0 12px 0 28px;
+    }
+    .el-color-picker--mini .el-color-picker__mask {
+        width: 60px;
+        height: 25px;
+    }
+    .el-form{
+        display: flex;
+        flex-direction: column;
+        margin:0 auto;
+        .option-box{
+            .option-item{
+                display: flex;
+                align-items: center;
+                margin-bottom:10px;
+                &:last-child{
+                    margin-bottom: 0;
+                }
+            }
+
+        }
+    }
+    
+    .dialog-footer{
+        text-align:center;
+        margin:30px 0 20px 0;
+    }
+}
+</style>

+ 44 - 8
src/views/chartRelevance_manage/components/saveChartTobaseDia.vue

@@ -34,7 +34,8 @@
                 label: 'ChartClassifyName',
                 label: 'ChartClassifyName',
                 value: 'ChartClassifyId',
                 value: 'ChartClassifyId',
                 children: 'Children',
                 children: 'Children',
-                emitPath: false
+                emitPath: false,
+                checkStrictly:isRelevanceChart
               }"
               }"
               style="width: 80%"
               style="width: 80%"
               :placeholder="$t('Edb.InputHolderAll.input_classify')"
               :placeholder="$t('Edb.InputHolderAll.input_classify')"
@@ -92,6 +93,9 @@ export default {
 					{ required: true, message: /* '图表分类不能为空' */this.$t('Chart.Vailds.classify_msg'), trigger: 'blur' },
 					{ required: true, message: /* '图表分类不能为空' */this.$t('Chart.Vailds.classify_msg'), trigger: 'blur' },
 				],
 				],
 			}
 			}
+		},
+		isRelevanceChart(){
+			return ['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)&&this.chartData.Source!==1
 		}
 		}
 	},
 	},
 	data () {
 	data () {
@@ -111,19 +115,31 @@ export default {
       let res = null;
       let res = null;
       if(this.chartData.Source===1) {
       if(this.chartData.Source===1) {
         res = await dataBaseInterface.chartClassify();
         res = await dataBaseInterface.chartClassify();
-      }else if(this.$route.path==='/relevancechartEditor'&&this.chartData.Source!==1) {
-        res = await chartRelevanceApi.classifyList();
+      }else if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)&&this.chartData.Source!==1) {
+        res = await chartRelevanceApi.classifyTree();
       }else if(this.$route.path==='/statisticFeatureChartEditor'&&this.chartData.Source!==1) {
       }else if(this.$route.path==='/statisticFeatureChartEditor'&&this.chartData.Source!==1) {
         res = await statisticFeatureInterface.classifyList();
         res = await statisticFeatureInterface.classifyList();
       }
       }
 
 
       if(res.Ret !== 200) return
       if(res.Ret !== 200) return
-      this.filterNodes(res.Data.AllNodes,this.chartData.Source===1?3:1)
+      if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)&&this.chartData.Source!==1){
+        this.filterNodesAll(res.Data.AllNodes)
+      }else{
+        this.filterNodes(res.Data.AllNodes,this.chartData.Source===1?3:1)
+      }
+      
 
 
 			this.classifyOptions = res.Data.AllNodes || [];
 			this.classifyOptions = res.Data.AllNodes || [];
     
     
 		},
 		},
-
+		filterNodesAll(arr){
+			arr.length && arr.forEach(item => {
+				item.Children && item.Children.length && this.filterNodesAll(item.Children)
+				if(!item.Children.length) {
+					delete item.Children
+				}
+			})
+		},
 		// 递归改变第三级目录结构
 		// 递归改变第三级目录结构
 		filterNodes(arr,n) {
 		filterNodes(arr,n) {
 			arr.length && arr.forEach(item => {
 			arr.length && arr.forEach(item => {
@@ -147,8 +163,14 @@ export default {
         4: 10
         4: 10
       }
       }
       let Source;
       let Source;
-      if(this.$route.path==='/relevancechartEditor') {
+      if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)) {
         Source = this.source;
         Source = this.source;
+        //多因子的参数和接口都不一样,这里直接返回
+        if(this.$parent.chartInfo.Model===2){
+            this.$emit('handleSave',{ChartName: name,ClassifyId: classify,type:this.saveScence})
+            return
+        }
+        
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
         Source = sourceMap[this.source]
         Source = sourceMap[this.source]
       }
       }
@@ -162,8 +184,22 @@ export default {
       }
       }
 
 
       let res = null;
       let res = null;
-      if(this.$route.path==='/relevancechartEditor') {
-        res = await chartRelevanceApi.saveChart(params)
+      if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)) {
+        //单因子-相关性额外参数
+        let otherParams = this.source===2?{
+            CorrelationExtraConfig:{
+                LegendConfig:this.$parent.chartBatchData.CorrelationData.YDataList.map(i=>{
+                    return {
+                        LegendName:i.Name,
+                        SeriesId:i.SeriesId,
+                        EdbInfoId:i.Id,
+                        Color:i.Color
+                    }
+                })
+            },
+            SourcesFrom:this.$parent.chartBatchData.SourcesFrom
+        }:{}
+        res = await chartRelevanceApi.saveChart({...params,...otherParams})
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
         res = await statisticFeatureInterface.saveChart(params);
         res = await statisticFeatureInterface.saveChart(params);
       } 
       } 

+ 2 - 2
src/views/chartRelevance_manage/components/saveEdbToBaseDia.vue

@@ -173,7 +173,7 @@ export default {
         4: 10
         4: 10
       }
       }
       let Source;
       let Source;
-      if(this.$route.path==='/relevancechartEditor') {
+      if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)) {
         Source = this.source;
         Source = this.source;
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
         Source = sourceMap[this.source]
         Source = sourceMap[this.source]
@@ -190,7 +190,7 @@ export default {
 			}
 			}
 
 
 			let res = null;
 			let res = null;
-      if(this.$route.path==='/relevancechartEditor') {
+      if(['/relevancechartEditor','/relevancechartEditorV2'].includes(this.$route.path)) {
         res = await chartRelevanceApi.saveEdb(params);
         res = await chartRelevanceApi.saveEdb(params);
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
       }else if(this.$route.path==='/statisticFeatureChartEditor') {
         res = await statisticFeatureInterface.saveEdb(params);
         res = await statisticFeatureInterface.saveEdb(params);

+ 5 - 0
src/views/chartRelevance_manage/components/selectTarget.vue

@@ -4,6 +4,7 @@
       style="width: 100%"
       style="width: 100%"
       v-model="targetType"
       v-model="targetType"
       :placeholder="$t('StatisticAnalysis.StatisticFeatureChart.selecr_indicator_pld')"
       :placeholder="$t('StatisticAnalysis.StatisticFeatureChart.selecr_indicator_pld')"
+      :disabled="isDisabled"
       @change="targetTypeChange"
       @change="targetTypeChange"
       v-if="selectStyleType===1&&filter"
       v-if="selectStyleType===1&&filter"
     >
     >
@@ -82,6 +83,7 @@
       :filterable="!search_txt"
       :filterable="!search_txt"
       remote
       remote
       clearable
       clearable
+      :disabled="isDisabled"
       :placeholder="$t('Edb.InputHolderAll.select_edb_name')"
       :placeholder="$t('Edb.InputHolderAll.select_edb_name')"
       :style="`width: ${width}; ${filter?'margin-top: 20px':''}`"
       :style="`width: ${width}; ${filter?'margin-top: 20px':''}`"
       :remote-method="searchHandle"
       :remote-method="searchHandle"
@@ -136,6 +138,9 @@ export default {
       },
       },
       width: {
       width: {
         default:'100%'
         default:'100%'
+      },
+      isDisabled:{
+        default:false
       }
       }
     },
     },
     watch:{
     watch:{

+ 4 - 1
src/views/chartRelevance_manage/css/index.scss

@@ -146,13 +146,16 @@ $normal-font: 14px;
               text-align: center;
               text-align: center;
               margin-bottom: 10px;
               margin-bottom: 10px;
             }
             }
-            .chart-author {
+            .chart-author,.chart-source {
               font-size: 14px;
               font-size: 14px;
               color: #333;
               color: #333;
               position: absolute;
               position: absolute;
               bottom: 0;
               bottom: 0;
               right: 50px;
               right: 50px;
             }
             }
+            .chart-source{
+                left:50px;
+            }
             .chartWrapper {
             .chartWrapper {
               position: relative;
               position: relative;
               .range-cont {
               .range-cont {

+ 10 - 2
src/views/chartRelevance_manage/mixins/classifyMixin.js

@@ -37,6 +37,14 @@ export default {
         let search_obj = this.searchOptions.find(
         let search_obj = this.searchOptions.find(
           (_) => _.ChartInfoId === newval
           (_) => _.ChartInfoId === newval
         );
         );
+        if(this.$route.path==='/chartrelevance'){
+            if(!search_obj) return 
+            // 重置筛选状态
+            this.select_id = newval;
+            this.select_node = search_obj.UniqueCode;
+            this.select_classify = 0;
+            return 
+        }
         let deep_arr = _.cloneDeep(this.treeData);
         let deep_arr = _.cloneDeep(this.treeData);
         // 查找图表的分类父级id
         // 查找图表的分类父级id
 
 
@@ -121,8 +129,8 @@ export default {
 				width > 500
 				width > 500
 					? 'auto'
 					? 'auto'
 					: width <= 260
 					: width <= 260
-					? 90
-					: 0.7 * width;
+					? 80
+					: 0.4 * width;
 			this.$set(node, 'Nodewidth', label_wid + 'px');
 			this.$set(node, 'Nodewidth', label_wid + 'px');
 		},200),
 		},200),
 
 

+ 210 - 0
src/views/chartRelevance_manage/relevance/components/batchSelectFormula.vue

@@ -0,0 +1,210 @@
+<template>
+    <div class="batch-select-formula-wrap">
+        <el-form :model="formulaForm" :rules="formulaRules" label-width="80px" ref="formulaForm">
+            <div class="formula-list">
+                <div class="list-item" v-for="(item,index) in formulaForm.CalculateStep" :key="index">
+                    <span>
+                        <img @click="deleteFormula(index)" style="width:15px;height:15px;cursor: pointer;" src="~@/assets/img/ai_m/delete.png" alt="">
+                    </span>
+                    <el-select v-model="item.formulaType">
+                        <el-option 
+                            v-for="option in formulaOpt"
+                            :key="option.Source"
+                            :value="option.Source"
+                            :label="option.CalculateName"
+                        />
+                    </el-select>
+                    <div class="form-item-box">
+                        <!-- N期 N等于-->
+                        <el-form-item
+                            v-if="[5,6,7].includes(item.formulaType)"
+                            :label="$t('EtaBasePage.label_n_val')"
+                            :prop="`CalculateStep[${index}].nNum`"
+                            :rules="{required:true,message:$t('Edb.Valids.n_msg'),trigger:'blur'}"
+                        >
+                            <el-input v-model="item.nNum" :placeholder="$t('Edb.InputHolderAll.input_n_value')" type="number"></el-input>
+                        </el-form-item>
+                        <!-- 超季节性 N等于 日历-->
+                        <el-form-item 
+                            v-if="item.formulaType===11"
+                            :label="$t('EtaBasePage.label_n_val')"
+                            :prop="`CalculateStep[${index}].nNum`"
+                            :rules="{required:true,message:$t('Edb.Valids.n_msg'),trigger:'blur'}"
+                        >
+                            <el-input v-model="item.nNum" :placeholder="$t('Edb.InputHolderAll.input_n_value')" type="number"></el-input>
+                        </el-form-item>
+                        <el-form-item 
+                            v-if="item.formulaType===11"
+                            :label="$t('EtaBasePage.label_calendar')"
+                        >
+                            <el-select v-model="item.Calendar">
+                                <el-option :label="$t('Chart.calendar_gre')" value="公历"></el-option>
+                                <el-option :label="$t('Chart.calendar_lunar')" value="农历"></el-option>
+                            </el-select>
+                        </el-form-item>
+                        <!-- 指数修匀 alpha值 -->
+                        <el-form-item
+                            v-if="item.formulaType===15"
+                            :label="$t('EtaBasePage.alpha_value_lable')" 
+                            :prop="`CalculateStep[${index}].alphaValue`"
+                            :rules="[
+                                {required:true,message:$t('Edb.Valids.alpha_msg'),trigger:'blur'},
+                                {validator:validator,trigger:['change','blur']}]">
+                            <el-input v-model.trim="item.alphaValue" style="width:140px" :placeholder="$t('Edb.InputHolderAll.input_alpha_val')" type="number"></el-input>
+                        </el-form-item>
+                    </div>
+                    
+                </div>
+            </div>
+            <div class="tool-box-wrap">
+                <!-- 添加计算公式 -->
+                <div class="add-btn" @click="addFormula">
+                        <img style="width:15px;height:15px;vertical-align: middle;" src="~@/assets/img/icons/formula-add.png" alt="">
+                    {{ $t('StatisticAnalysis.ChartRelevance.formula_add_btn') }}
+                </div>
+                <div class="input-box">
+                    <el-form-item
+                        required
+                        :label="$t('StatisticAnalysis.ChartRelevance.series_name')"
+                        label-width="120px"
+                    >
+                        <el-input v-model.trim="formulaForm.SeriesName" style="width:230px" 
+                        :placeholder="$t('StatisticAnalysis.ChartRelevance.series_name_placeholder')"></el-input>
+                    </el-form-item>
+                </div>
+            </div>
+        </el-form>
+        
+    </div>
+</template>
+
+<script>
+export default {
+    props:{
+        formulaOpt:{
+            type:Array,
+            default:[]
+        },
+        factorData:{
+            type:Object,
+            default:{}
+        }
+    },
+    computed:{
+        //计算方式列表-改为后端获取
+        /* formulaOption(){
+            return [
+                {
+                    value:5,
+                    label:this.$t('Edb.CalculatesAll.on_year'),//同比值
+                },
+                {
+                    value:7,
+                    label:this.$t('Edb.CalculatesAll.differ'),//同差值
+                },
+                {
+                    value:8,
+                    label:this.$t('Edb.CalculatesAll.rule_move_average'),//N期移动均值
+                },
+                {
+                    value:12,
+                    label:this.$t('Edb.CalculatesAll.period_over_period'),//N期环比值
+                },
+                {
+                    value:13,
+                    label:this.$t('Edb.CalculatesAll.period_difference'),//N期环差值
+                },
+                {
+                    value:35,
+                    label:this.$t('Edb.CalculatesAll.super_seasonal'),//超季节性
+                },
+                {
+                    value:72,
+                    label:this.$t('Edb.CalculatesAll.ex_smooth'),//指数修匀
+                },
+            ]
+        } */
+    },
+    watch:{
+        factorData:{
+            handler(newValue){
+                this.formulaForm = _.cloneDeep(newValue)
+            },
+            immediate:true
+        },
+    },
+    data() {
+        return {
+            formulaForm:{
+                CalculateStep:[],
+                SeriesName:''
+            },
+            formulaRules:{},
+        };
+    },
+    methods: {
+        //打开弹窗时,在外层组件调用
+        initFormulaList(){
+            //this.formulaForm.CalculateStep = _.cloneDeep(this.dataFormulaList)
+            this.getFormulaOption()
+        },
+        validator(rule,value,callback){
+            if(Number(value)<=0||Number(value)>=1){
+                callback(new Error(this.$t('Edb.Valids.alpha_value_vaild')))
+            }else{
+                callback()
+            }
+        },
+        addFormula(){
+            this.formulaForm.CalculateStep.push({
+                formulaType:5,
+                Calendar:'公历',
+                nNum:1,
+                alphaValue:0.5
+            })
+        },
+        deleteFormula(index){
+            if(this.formulaForm.CalculateStep.length>=5){
+                return this.$message.warning(/* "最多仅能添加5个计算公式" */ this.$t('StatisticAnalysis.ChartRelevance.formulaform_add_hint'))
+            }
+            this.formulaForm.CalculateStep.splice(index,1)
+        },
+        async checkForm(){
+            return await this.$refs.formulaForm.validate()
+        },
+    },
+};
+</script>
+
+<style lang="scss">
+.batch-select-formula-wrap{
+    .el-input,.el-select{
+        width:140px;
+    }
+    .formula-list{
+        margin-top: 20px;
+        .list-item{
+            margin-bottom: 15px;
+            display: flex;
+            align-items: center;
+            gap:0 15px;
+            .form-item-box{
+                display: flex;
+                .el-form-item{
+                    margin-bottom: 0;
+                }
+            }
+        }
+    }
+    .tool-box-wrap{
+        margin-top: 20px;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .add-btn{
+            color:#0052D9;
+            cursor: pointer;
+        }
+    }
+}
+</style>

+ 257 - 0
src/views/chartRelevance_manage/relevance/components/batchSelectTable.vue

@@ -0,0 +1,257 @@
+<template>
+    <!-- 封装表格跨页多选逻辑 -->
+    <div class="batch-select-table">
+        <div class="table-box" style="width:50%;">
+            <el-table
+                border height="500px"
+                v-loading="tableLoading"
+                ref="tableRef" 
+                :data="tableData"
+                @select="selectHandle" 
+                @select-all="selectAllHandle"
+            >
+                <el-table-column type="selection" min-width="50" align="center"/>
+                <el-table-column 
+                    align="center"
+                    v-for="column in tableColumns" 
+                    :key="column.key"
+                    :label="column.label"
+                    :prop="column.key"
+                    :width="column.width"
+                    :show-overflow-tooltip="column.showOverflowTooltip">
+                    <template slot-scope="{row}">
+                        <span v-if="column.key==='Frequency'">{{ getFrequencyTrans(row.Frequency) }}</span>
+                        <span v-else-if="['UniEn','Unit'].includes(column.key)">{{ getUnitTrans(row.Unit) }}</span>
+                        <span v-else>{{ row[column.key] }}</span>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <m-page 
+                style="margin-top:10px"
+                class="table-page" 
+                v-show="tableParams.total"
+                :total="tableParams.total" 
+                :pageSize="tableParams.pageSize"
+                :page_no="tableParams.pageNo"
+                :pagercount="tableParams.pagerCount"
+                @handleCurrentChange="pageNumberChange"
+            />
+        </div>
+        <!-- 加入已选指标 -->
+        <el-button type="primary" @click="handleAddSelectData">{{ $t('EtaBasePage.add_to_selections') }}</el-button>
+        <div class="select-box" style="width: 35%;">
+            <el-table
+                border height="500px"
+                ref="selectRef" 
+                :data="selectData"
+            >
+            <el-table-column
+                align="center"
+                :label="tableColumns[0].label"
+                :prop="tableColumns[0].key"
+             />
+             <el-table-column width="50px" align="center" v-if="selectData.length">
+                <template slot="header" slot-scope="scope">
+                    <img @click="handleDelSelect(_,'all')" style="width:15px;height:15px;cursor: pointer;" src="~@/assets/img/ai_m/delete.png" alt="">
+                </template>
+                <template slot-scope="{row,$index}">
+                    <img @click="handleDelSelect($index,'')" style="width:15px;height:15px;cursor: pointer;" src="~@/assets/img/ai_m/delete.png" alt="">
+                </template>
+            </el-table-column>
+        </el-table>
+        </div>
+    </div>
+</template>
+
+<script>
+import mPage from '@/components/mPage.vue'
+export default {
+    components:{mPage},
+    props:{
+        tableData:{
+            type:Array,
+            default:[]
+        },
+        tableLoading:{
+            type:Boolean,
+            default:false
+        },
+        tableColumns:{
+            type:Array,
+            default:[]
+        },
+        tableParams:{
+            type:Object,
+            default:()=>{
+                return {
+                    total:0,
+                    pageSize:20,
+                    pageNo:1,
+                    pagerCount:5,
+                    uniqueKey:'Id'
+                }
+            }
+        },
+        isSelectAll:{
+            type:Boolean,
+            default:false,
+        },
+        factorData:{
+            type:Object,
+            default:{}
+        }
+    },
+    watch:{
+        /* isSelectAll(newValue){
+            this.listCheckAllChange(newValue)
+        }, */
+        factorData:{
+            handler(newValue){
+                this.selectData = _.cloneDeep(newValue.EdbMappings||[])
+            },
+            immediate:true,
+            deep:true
+        }
+        
+    },
+    data() {
+        return {
+            selectList:[],//左侧表格已选择/已剔除的数据
+            selectData:[],//右侧表格的数据
+            selectionReactCancel:false,
+        };
+    },
+    methods: {
+        //点击数据行的checkbox触发 selection:已选择的所有数据行;row:当前点击的数据行
+        selectHandle(selection,row){
+            const {uniqueKey} = this.tableParams
+            //通过判断selection中有无row确定是勾选了checkbox还是取消勾选
+            //当为勾选时,判断selectList是否表示已选择的数据(isSelectAll===false),若不是则不加入
+            //当为取消勾选时,判断selectList是否表示已剔除的数据(isSelectAll===true),若不是则不加入
+            let check = selection.some(i=>i[uniqueKey] == row[uniqueKey])?(!this.isSelectAll):(this.isSelectAll)
+            if(check){
+                this.selectList.push(row[uniqueKey])
+            }else{
+                this.selectList = this.selectList.filter(i=>i!=row[uniqueKey])
+            }
+            this.selectionChange()
+        },
+        //点击表头全选 or 调用toggleAllSelection 触发
+        selectAllHandle(selection){
+            //当前table的所有数据
+            const tableIds = this.tableData.map(it => it[this.tableParams.uniqueKey])
+            //通过判断selection中有无数据确定是全选or取消全选
+            //当为全选时,判断selectList是否表示已选择的数据(isSelectAll===false),若不是则从selectList中剔除
+            //当为取消全选时,判断selectList是否表示已剔除的数据(isSelectAll===true),若不是则从selectList中剔除
+            let check = selection&&selection.length?(!this.isSelectAll):this.isSelectAll
+            if(check){
+                this.selectList =  [...this.selectList,...tableIds]
+            }else{
+                this.selectList = this.selectList.filter(it => !tableIds.includes(it))
+            }
+            this.selectionChange()
+        },
+        //当勾选项改变时 处理数据
+        selectionChange(){
+            //去重 selectList
+            let duplicateArr = Array.from(new Set(this.selectList))
+            let isCheckAll = false,isIndeterminate = false
+            //判断已选择的数据,更改列表全选checkbox的状态
+            /**
+             * 全选:
+             * 1.selectList.length === total && isSelectAll === false (已选择的数据为全部数据)
+             * 2.selectList.length === 0 && isSelectAll === true (已剔除的指标为空)
+             * 以上两种满足其一即可
+             */
+            const selectAll = duplicateArr.length===this.tableParams.total && (!this.isSelectAll)
+                || duplicateArr.length===0 && this.isSelectAll
+            /**
+             * 全不选:
+             * 1.selectList.length === total && isSelectAll === true (剔除了全部数据)
+             * 2.selectList.length === 0 && isSelectAll === false (没选择任何数据)
+             * 以上两种满足其一即可
+             */
+            const selectNone = duplicateArr.length===this.tableParams.total && this.isSelectAll
+                || duplicateArr.length===0 && (!this.isSelectAll)
+            //其余情况均为半选
+
+            if(selectAll){
+                isCheckAll = true
+                isIndeterminate = false
+            }else if(selectNone){
+                isCheckAll = false
+                isIndeterminate = false
+            }else{
+                isCheckAll = false
+                isIndeterminate = true
+            }
+
+            this.$emit('changeCheckAll',{isCheckAll,isIndeterminate})
+        },
+        //切换页面时调整勾选项 (在外层组件调用)
+        adjustSelection(){
+            const {uniqueKey} = this.tableParams
+            this.$refs.tableRef.clearSelection()
+            this.tableData.forEach(data=>{
+                if(this.selectList.includes(data[uniqueKey])){
+                    //isSelectAll===false,selectList为已选择的数据,则对应数据行打勾
+                    //isSelectAll === true,selectList为已剔除的数据,则对应的数据行取消打勾
+                    this.$nextTick(()=>{
+                        this.$refs.tableRef.toggleRowSelection(data,!this.isSelectAll)
+                    })
+                }else{
+                    this.$nextTick(()=>{
+                        this.$refs.tableRef.toggleRowSelection(data,this.isSelectAll)
+                    })
+                }
+            })
+        },
+        listCheckAllChange(newValue){
+            this.selectList = []
+            this.$refs.tableRef && this.tableData.length && this.$refs.tableRef.clearSelection()
+            if(newValue){
+                //会触发select-all
+                this.$refs.tableRef && this.tableData.length && this.$refs.tableRef.toggleAllSelection()
+                /* //不会触发select-all
+                this.$refs.tableRef && this.tableData.length && this.$refs.tableRef.toggleRowSelection(this.tableData[0],true) */
+            }
+        },
+        pageNumberChange(page){
+            this.$emit('pageChange',page)
+        },
+        handleAddSelectData(){
+            //外层组件处理校验和添加逻辑
+            this.$emit('addSelectData',{selectList:this.selectList,selectData:this.selectData})
+
+        },
+        //外层组件调用
+        addSelectData(data){
+            this.selectData = _.cloneDeep(data)
+            
+        },
+        handleDelSelect(index,type){
+            if(type==='all'){
+                return (this.selectData = [])
+            }
+            this.selectData.splice(index,1)
+        },
+    },
+};
+</script>
+
+<style scoped lang="scss">
+.batch-select-table{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    gap:0 20px;
+    .table-box,.select-box{
+        flex:1;
+    }
+    &::after{
+        content:'';
+        flex:0 0 auto;
+        visibility: hidden;
+    }
+}
+</style>

+ 38 - 0
src/views/chartRelevance_manage/relevance/components/formMixin.js

@@ -0,0 +1,38 @@
+//单因子,多因子的表单 共同逻辑
+import {yearSelector} from '@/utils/defaultOptions';
+import selectTarget from '../../components/selectTarget.vue'
+export default{
+    components:{selectTarget},
+    props:{
+        chartInfoData:{
+            type:Object,
+            default:{}
+        },
+        infoForm:{
+            type:Object,
+            default:()=>{return {Curve:{},Correlation:{},RollingCorrelation:[]}}
+        }
+    },
+    computed:{
+        yearSelector(){
+            return [
+                ...yearSelector,
+                { name: this.$i18n.locale == 'zh'?'自定义':'custom',value: 5 }
+            ]
+        },
+        dayOpt(){
+            return [
+                {label:this.$t('Edb.FreAll.year_min'),val:'年'},
+                {label:this.$t('Edb.FreAll.quarter_min'),val:'季'},
+                {label:this.$t('Edb.FreAll.month_min'),val:'月'},
+                {label:this.$t('Edb.FreAll.week_min'),val:'周'},
+                {label:this.$t('Edb.FreAll.day_min'),val:'天'},
+            ]
+        }
+    },
+    methods: {
+        handleSelectTarget(type='A',target=''){
+            this.$emit(`selectTarget`,{type,target})
+        },
+    },
+}

+ 137 - 0
src/views/chartRelevance_manage/relevance/components/modifyClassifyDialog.vue

@@ -0,0 +1,137 @@
+<template>
+    <div class="Dialog-box">
+        <el-dialog
+        :visible.sync="isOpenDialog"
+        :close-on-click-modal="false"
+        :modal-append-to-body='false'
+        @close="cancelHandle"
+        custom-class="dialog"
+        center
+        width="560px"
+        v-dialogDrag>
+            <div slot="title" style="display:flex;alignItems:center;">
+                <img :src="type=='add'?$icons.add:$icons.edit" style="color:#fff;width:16px;height:16px;marginRight:5px;">
+                <span style="fontSize:16px;">{{type==='add' ? $t('Table.add_btn') : $t('Table.edit_btn')}}</span>
+            </div>
+            <div class="dialog-main">
+                <el-form
+                ref="diaForm"
+                label-position="left"
+                hide-required-asterisk
+                label-width="120px"
+                :model="formData"
+                :rules="formRules">
+                    <el-form-item :label="$t('OnlineExcelPage.parent_directory_lable')" v-if="formData.level>0">
+                        <el-tooltip class="item" effect="dark" :content="getParentName" placement="top">
+                            <span class="parentStr">{{getParentName}}</span>
+                        </el-tooltip>
+                    </el-form-item>
+                    <el-form-item :label="$t('EtaBasePage.menu_name')" prop="levelVal">
+                        <el-input
+                        v-model="formData.levelVal"
+                        style="width: 80%"
+                        :placeholder="$t('Dialog.require_vaild')"></el-input>
+                    </el-form-item>
+                </el-form>
+            </div>
+            <div class="dia-bot">
+                <el-button type="primary" style="margin-right:20px" @click="saveHandle"><!-- 保存 -->{{$t('Dialog.confirm_save_btn')}}</el-button>
+                <el-button type="primary" plain @click="cancelHandle"><!-- 取消 -->{{$t('Dialog.cancel_btn')}}</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import chartRelevanceApi from "@/api/modules/chartRelevanceApi.js";
+export default {
+    props: {
+        isOpenDialog: {
+            type: Boolean,
+        },
+        type:{
+            type:String,
+            default:'add'
+        },
+        formData: {
+            type: Object,//{parentArr父级数据}
+        }
+    },
+    computed:{
+        getParentName(){
+            const arr=this.formData.parentArr||['无']
+            let strArr=arr.reverse().map(item=>{
+                return item.classifyName
+            })
+            
+            return strArr.join('/')
+        }
+    },
+    data () {
+        return {
+            formRules: {
+                levelVal:[
+                    { required: true, message: this.$t('EtaBasePage.input_menu_msg'), trigger: 'blur' },
+                ],
+            },
+            options:  [],
+
+        };
+    },
+    methods: {
+        async saveHandle() {
+            await this.$refs.diaForm.validate();
+            let addParams = {
+                ChartClassifyName:this.formData.levelVal,
+                Level:this.formData.level,
+                ParentId:this.formData.parent_id,
+            }
+            let editParams = {
+                ChartClassifyName:this.formData.levelVal,
+                ChartClassifyId:this.formData.nodeId
+            }
+            const res = this.type==='add'?await chartRelevanceApi.classifyAdd(addParams):await chartRelevanceApi.classifyEdit(editParams)
+            if(res.Ret!==200) return 
+            this.$message.success(`${this.type==='add'?'新增':'编辑'}成功`)
+            this.callbackHandle()
+
+
+        },
+        /* 成功回调 */
+        callbackHandle(type) {
+            this.$refs.diaForm.resetFields();
+            this.$emit('sucessCallback',type)
+        },
+        /* 取消 */
+        cancelHandle() {
+            this.$refs.diaForm.resetFields();
+            this.$emit('closeDia')
+        },
+    },
+    created() {},
+    mounted() {},
+}
+</script>
+<style lang='scss'>
+.Dialog-box {
+    .parentStr{
+        display: block;
+        width: 304px;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+    }
+    .dialog-main {
+        padding-left: 50px;
+    }
+    .el-cascader .el-input {
+        width: 100%;
+    }
+    .dia-bot {
+        margin: 52px 0 30px;
+        display: flex;
+        justify-content: center;
+
+    }
+    }
+</style>

+ 616 - 0
src/views/chartRelevance_manage/relevance/components/multipleIndForm.vue

@@ -0,0 +1,616 @@
+<template>
+    <div class="multiple-model-form model-form">
+        <!-- 标的指标 -->
+        <el-form-item 
+            :label="$t('StatisticAnalysis.ChartRelevance.target_indicator')" 
+            prop="IndTarget" class="select-target">
+            <!-- 添加后的图表不允许修改标的指标 -->
+            <selectTarget 
+                :isDisabled="isMultipleChartAdd"
+                :defaultId="chartInfoData.EdbInfoList?chartInfoData.EdbInfoList[0].EdbInfoId:''"
+                :defaultOpt="chartInfoData.EdbInfoList?[chartInfoData.EdbInfoList[0]]:[]" 
+                :defaultType="chartInfoData.EdbInfoList?chartInfoData.EdbInfoList[0].EdbInfoCategoryType:''"
+                @select="(target)=>handleSelectTarget('IndTarget',target)"
+            />
+        </el-form-item>
+        <!-- 因子指标系列 -->
+        <div class="factor-form-item">
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.factor_indicators')" required>
+            </el-form-item>
+            <div class="factor-list">
+                    <div class="list-item" 
+                        v-for="(item,index) in factorList" :key="index">
+                        <span>
+                            <!-- <i class="el-icon-arrow-right"></i> -->
+                            {{ item.SeriesName }}
+                        </span>
+                        <span  @click.stop="openAddDialog(item)" style="margin-left: auto;">
+                            <img src="~@/assets/img/icons/edit_blue_new.png" alt="" style="width: 16px; height: 16px; margin-right: 5px">
+                        </span>
+                        <span @click="deleteFactorIndicators(index)">
+                            <img src="~@/assets/img/set_m/del_icon.png" alt="" style="width: 14px; height: 14px;">
+                        </span>
+                    </div>
+            </div>
+            <div class="add-factor-btn" @click="openAddDialog(null)">
+                <img src="~@/assets/img/add-quadrate-blue.png" />
+                {{ $t('StatisticAnalysis.ChartRelevance.add_factor_indicators') }}
+            </div>
+        </div>
+        <div class="form-box">
+            <!-- 相关性 -->
+            <div class="label-title">{{ $t('Chart.ChartType.correlation_name') }}</div>
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.calculation_window')"
+                prop="Correlation.CalculateValue" class="flex-form-item">
+                <el-input
+                    style="flex:2"
+                    :step="1"
+                    type="number"
+                    v-model="infoForm.Correlation.CalculateValue"
+                    @change="val => { infoForm.Correlation.CalculateValue = Number(val); }"
+                />
+                <el-select
+                        style="flex:2"
+                        v-model="infoForm.Correlation.CalculateUnit">
+                    <el-option
+                        v-for="item in dayOpt"
+                        :key="item.val"
+                        :label="item.label"
+                        :value="item.val"
+                    />
+                </el-select>
+            </el-form-item>
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.analysis_cycle')" 
+                prop="Correlation.LeadValue" class="flex-form-item">
+                <el-input
+                    style="flex:2"
+                    :step="1"
+                    type="number"
+                    v-model="infoForm.Correlation.LeadValue"
+                    @change="val => { infoForm.Correlation.LeadValue = Number(val); }"
+                />
+                <el-select
+                    style="flex:2"
+                    v-model="infoForm.Correlation.LeadUnit">
+                    <el-option
+                        v-for="item in dayOpt"
+                        :key="item.val"
+                        :label="item.label"
+                        :value="item.val"
+                    />
+                </el-select>
+            </el-form-item>
+        </div>
+        <!-- 添加/编辑因子系列弹窗 -->
+        <el-dialog
+            :visible.sync="isAddFactorDialogShow"
+            :close-on-click-modal="false"
+            :modal-append-to-body='false'
+            @close="isAddFactorDialogShow=false"
+            center width="75%" top="30px"
+            :title="$t('StatisticAnalysis.ChartRelevance.add_factor_indicators')"
+            custom-class="add-factor-dialog"
+        >
+            <div class="dialog-content">
+                <!-- 选择指标 or 预测指标 -->
+                <div class="table-radio-wrap">
+                    <el-radio-group v-model="factorData.EdbInfoType" @input="changeEdbType">
+                        <el-radio :label="0">ETA指标</el-radio>
+                        <el-radio :label="1">预测指标</el-radio>
+                    </el-radio-group>
+                    
+                </div>
+                <!-- 表格筛选项 -->
+                <div class="table-select-box">
+                    <el-cascader
+                        v-model="tableSelectParams.classify"
+                        :options="classifyOpt"
+                        :props="{
+                            label: 'ClassifyName',
+                            value: 'ClassifyId',
+                            children: 'Children',
+                            multiple: true,
+                            emitPath:false
+                        }"
+                        clearable collapse-tags
+                        :placeholder="$t('EtaBasePage.label_classify')"
+                        @change="handleFilter"
+                    />
+                    <el-select
+                        v-model="tableSelectParams.frequency"
+                        :placeholder="$t('EtaBasePage.select_frequency')"
+                        clearable multiple collapse-tags
+                        @change="handleFilter">
+                        <el-option
+                            v-for="item in frequencyArr"
+                            :key="item.value"
+                            :label="item.label"
+                            :value="item.value"
+                        >
+                        </el-option>
+                    </el-select>
+                    <el-cascader
+                        v-model="tableSelectParams.creator"
+                        :placeholder="$t('EtaBasePage.table_col_creator')"
+                        :options="sysUserOpt"
+                        :props="{
+                            value: 'AdminId',
+                            label: 'RealName',
+                            children: 'ChildrenList',
+                            multiple: true,
+                            emitPath:false
+                        }"
+                        collapse-tags
+                        :show-all-levels="false"
+                        clearable filterable
+                        @change="handleFilter"
+                    />
+                    <el-input 
+                        :placeholder="$t('Edb.InputHolderAll.input_name_orid')" 
+                        v-model="tableSelectParams.keyword"
+                        style="width: 240px"
+                        @keydown.enter.native="handleFilter"
+                    >
+                        <i slot="prefix" class="el-input__icon el-icon-search"></i>
+                    </el-input>
+                    <el-checkbox 
+                        :label="$t('EtaBasePage.label_all_check')"
+                        :indeterminate="isIndeterminate" 
+                        v-model="isCheckAll" 
+                        @change="listCheckAllChange"/>
+                </div>
+                <!-- 表格 -->
+                <batchSelectTable
+                    ref="batchSelectTable"
+                    :tableColumns="tableColumns"
+                    :tableData="tableData"
+                    :tableParams="tableParams"
+                    :tableLoading="tableLoading"
+                    :isSelectAll="isSelectAll"
+                    :factorData="factorData"
+                    @pageChange="tablePageChange"
+                    @changeCheckAll="changeCheckAll"
+                    @addSelectData="addSelectData"
+                ></batchSelectTable>
+                <!-- 计算公式 -->
+                <batchSelectFormula
+                    ref="batchSelectFormula"
+                    :factorData="factorData"
+                    :formulaOpt="formulaOpt"
+                ></batchSelectFormula>
+            </div>
+            <div class="dialog-footer">
+                <el-button type="primary" plain @click="isAddFactorDialogShow=false">{{$t('Dialog.cancel_btn')}}</el-button>
+                <el-button type="primary" @click="handleAddFactor" :loading="addFactorLoading">{{$t('Dialog.confirm_btn')}}</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import batchSelectTable from './batchSelectTable'
+import batchSelectFormula from './batchSelectFormula'
+import formMixin from './formMixin'
+import { frequencySelectList } from '@/utils/defaultOptions'
+import {checkListChange,checkStepsChange} from '../utils/index'
+
+import { dataBaseInterface,departInterence } from '@/api/api.js'
+import * as preDictEdbInterface from "@/api/modules/predictEdbApi.js"
+import chartRelevanceApi from '@/api/modules/chartRelevanceApi'
+export default {
+    mixins:[formMixin],
+	components: { batchSelectTable, batchSelectFormula },
+    props:{
+        SeriesList:{
+            type:Object,
+            default:[]
+        },
+        isMultipleChartAdd:{
+            type:Boolean,
+            default:false
+        }
+    },
+    computed:{
+        tableColumns(){
+            return [
+                {
+                    key:this.$i18nt.locale==='en'?'EdbNameEn':'EdbName',
+                    label:this.$t('EtaBasePage.full_metric_name'),//指标全称
+                    width:'240px',
+                    showOverflowTooltip:true,
+                },
+                {
+                    key:'EndDate',
+                    label:this.$t('Edb.Detail.e_latest_date'),//最新日期
+                    width:'120px',
+                },
+                {
+                    key:'EndValue',
+                    label:this.$t('Edb.Detail.e_latest_value'),//最新值
+                    width:'80px',
+                    showOverflowTooltip:true,
+                },
+                {
+                    key:'SysUserRealName',
+                    label:this.$t('EtaBasePage.table_col_creator'),//创建人
+                    width:'80px',
+                    showOverflowTooltip:true,
+                },
+                {
+                    key:'Frequency',
+                    label:this.$t('Edb.Detail.e_fre'),//频率
+                    width:'50px',
+                    showOverflowTooltip:true,
+                },
+                {
+                    key:this.$i18nt.locale==='en'?'UnitEn':'Unit',
+                    label:this.$t('Edb.Detail.e_unit'),//单位
+                    width:'50px',
+                    showOverflowTooltip:true,
+                },
+            ]
+        },
+        frequencyArr(){
+            return frequencySelectList()
+        },
+    },
+    data(){
+        return {
+            factorList:[], //多因子系列列表
+            isAddFactorDialogShow:false,
+            addFactorLoading:false,
+            factorData:{
+                SeriesName:'',//因子系列名称
+                CalculateStep:[],//因子系列计算公式
+                EdbMappings:[],//因子系列选择的指标
+                EdbInfoType:0,//选择的指标类型:0指标1预测指标
+            },
+            tableData:[],
+            tableSelectParams:{
+                classify:'',
+                frequency:'',
+                creator:'',
+                keyword:'',
+            },
+            classifyOpt:[],
+            sysUserOpt:[],
+            formulaOpt:[],
+            tableParams:{
+                total:0,
+                pageSize:20,
+                pageNo:1,
+                pagerCount:5,
+                uniqueKey:'EdbInfoId',//数据行的唯一key
+            },
+            tableLoading:false,
+            isIndeterminate:false,//与isCheckAll一起表示列表全选的状态
+            isCheckAll:false,//与isIndeterminate一起表示列表全选的状态
+            isSelectAll:false,//是否勾选了列表全选:为true时,selectList是剔除的指标,为false时selectList是已选择的指标
+        }
+    },
+    methods:{
+        openAddDialog(data){
+            if(data){
+                this.factorData = _.cloneDeep(data)
+            }else{
+                this.factorData = {
+                    SeriesName:'',
+                    CalculateStep:[],
+                    EdbMappings:[],
+                    EdbInfoType:0,
+                }
+            }
+            
+            this.getClassifyOpt()
+            this.getSysUserOpt()
+            this.getFormulaOption()
+            //清空筛选项
+            this.initSelectOpt()
+            this.tableData = []
+            this.isAddFactorDialogShow = true
+        },
+        changeEdbType(){
+            if(this.isAddFactorDialogShow){
+                //若切换指标类型,清空选项
+                this.factorData.SeriesName = ''
+                this.factorData.EdbMappings = []
+                this.factorData.CalculateStep =[]
+                this.getFormulaOption()
+                this.getClassifyOpt()
+            }
+        },
+        initSelectOpt(){
+            this.tableSelectParams = {
+                classify:'',
+                frequency:'',
+                creator:'',
+                keyword:'',
+            }
+            this.tableParams = {
+                total:0,
+                pageSize:20,
+                pageNo:1,
+                pagerCount:5,
+                uniqueKey:'EdbInfoId',//数据行的唯一key
+            }
+            this.isIndeterminate = false
+            this.isCheckAll = false
+            this.listCheckAllChange(false)
+        },
+        async getClassifyOpt(){
+            const res= this.factorData.EdbInfoType===0
+                        ?await dataBaseInterface.menuListV3()
+                        :await preDictEdbInterface.classifyListV2()
+            if (res.Ret !== 200) return
+
+            const filterNodes = (arr)=>{
+                arr.length &&
+                    arr.forEach((item) => {
+                        item.Children.length && filterNodes(item.Children);
+                        if (!item.Children.length) {
+                            delete item.Children;
+                        }
+                    });
+            }
+            filterNodes(res.Data.AllNodes||[]);
+
+            this.classifyOpt = res.Data.AllNodes || [];
+        },
+        async getSysUserOpt(){
+            const res = await departInterence.getQuestionAdminList();
+            if (res.Ret === 200) {
+                this.sysUserOpt = res.Data.List||[];
+            }
+        },
+        async getFormulaOption(){
+            const res = await chartRelevanceApi.getCalculateFormula({
+                EdbInfoType:this.factorData.EdbInfoType
+            })
+            if(res.Ret===200){
+                this.formulaOpt = res.Data||[]
+            }
+        },
+        handleFilter(){
+            this.tableParams.pageNo = 1
+            this.tableData = []
+            this.getTableData()
+        },
+        listCheckAllChange(value){
+            this.isSelectAll = value
+            this.$nextTick(()=>{
+                this.$refs.batchSelectTable&&this.$refs.batchSelectTable.listCheckAllChange(value)
+            })
+        },
+        changeCheckAll({isCheckAll,isIndeterminate}){
+            this.isCheckAll = isCheckAll
+            this.isIndeterminate = isIndeterminate
+        },
+        tablePageChange(page){
+            this.tableParams.pageNo = page
+            this.getTableData('pageChange')
+        },
+        async addSelectData({selectList=[],selectData=[]}){
+            //校验:没选择任何指标
+            if(!this.isCheckAll&&!this.isIndeterminate){
+                return this.$message.warning(this.$t('StatisticAnalysis.ChartRelevance.batch_select_hint'))
+            }
+            //通过接口获取选中的所有指标
+            const {classify,frequency,creator,keyword} = this.tableSelectParams
+            const ClassifyIds = classify?classify.join(','):''
+            const Frequency = frequency?frequency.join(','):''
+            const SysUserIds = creator?creator.join(','):''
+            const res=await dataBaseInterface.getBatchFilterAddEdbList({
+                SysUserIds,ClassifyIds,
+                Keyword:keyword,
+                Frequency,
+                SelectAll:this.isSelectAll,
+                EdbInfoIds:selectList.join(','),
+                EdbInfoType:Number(this.factorData.EdbInfoType||0)
+            })
+            if(res.Ret!==200) return 
+            //去重
+            //const data = this.mergeAndDistinct(selectData,res.Data.SearchItem||[])
+            const data = selectData.concat(res.Data.SearchItem||[]).filter((item,index,self)=>{
+                return index ===self.findIndex(t=>t.EdbInfoId === item.EdbInfoId)
+            })||[]
+            //加入到selectData中
+            this.$refs.batchSelectTable.addSelectData(data)
+            //自动填写指标系列名称
+            if(!this.$refs.batchSelectFormula.formulaForm.SeriesName){
+                this.$refs.batchSelectFormula.formulaForm.SeriesName = selectData[0]?selectData.EdbName:data[0]?data[0].EdbName:''
+            }
+        },
+        async getTableData(type){
+            this.tableLoading=true
+            const {classify,frequency,creator,keyword} = this.tableSelectParams
+            const ClassifyIds = classify?classify.join(','):''
+            const Frequency = frequency?frequency.join(','):''
+            const SysUserIds = creator?creator.join(','):''
+            //没有任何筛选项时不展示数据
+            if(!ClassifyIds&&!Frequency&&!SysUserIds&&!keyword){
+                this.tableLoading = false
+                this.tableData = []
+                this.tableParams.total = 0
+                this.isIndeterminate = false
+                this.isCheckAll = false
+                return 
+            }
+            const res=await dataBaseInterface.getBatchAddEdbSearchList({
+                CurrentIndex:this.tableParams.pageNo,
+                PageSize: this.tableParams.pageSize,
+                SysUserIds,ClassifyIds,Frequency,
+                Keyword:keyword,
+                NotFrequency:'',
+                EdbInfoType:Number(this.factorData.EdbInfoType||0)
+            })
+            this.tableLoading=false
+            if(res.Ret!==200) return 
+
+            this.tableData=res.Data.SearchItem||[]
+            this.tableParams.total=res.Data.Paging.Totals||0
+
+            if(type==='pageChange'){
+                this.$nextTick(()=>{
+                    this.$refs.batchSelectTable.adjustSelection()
+                })
+            }else{
+                this.listCheckAllChange(true)
+            }
+        },
+        async handleAddFactor(){
+            //校验 是否选择了指标
+            //校验 计算公式填写
+            await this.$refs.batchSelectFormula.checkForm()
+            //是否超过100个指标
+            if(!this.checkLimitFactor()){
+                return this.$message.warning('所有因子指标已超过100个,请检查')
+            }
+            this.addFactorIndicators()
+        },
+        checkLimitFactor(){
+            let total = 0
+            //已添加过的因子系列
+            this.factorList.forEach(i=>total += i.EdbMappings.length)
+            //当前要添加的因子系列
+            const selectData = this.$refs.batchSelectTable.selectData
+            if(!this.factorData.SeriesId){
+                total+=selectData.length
+            }else{
+                const currentFactor = this.factorList.find(i=>i.SeriesId===this.factorData.SeriesId)
+                const EdbNum = (currentFactor&&currentFactor.EdbMappings.length)||0
+                total -=EdbNum
+                total+=selectData.length
+            }
+            return total<=100
+        },
+        //格式化因子系列计算方式的格式
+        formattingStep(steps=[],isTrans=true){
+            return isTrans
+                ?steps.map((step,index)=>{ //接口需要的格式
+                    const {formulaType,nNum,alphaValue,Calendar} = step
+                    return {
+                        Sort:index+1,
+                        Source:formulaType,
+                        Formula:(formulaType===15?alphaValue:nNum)+'', //N值取nNum,alpha值取alphaValue
+                        Calendar:formulaType===11?Calendar:'',//超季节性取值,其他为空
+                    }
+                })
+                :steps.map(step=>{ //回显需要的格式
+                    const {Source,Calendar,Formula} = step
+                    return {
+                        formulaType:Source,
+                        nNum:Source===15?1:Formula,
+                        alphaValue:Source===15?Formula:0.5,
+                        Calendar
+                    }
+                })
+        },
+        //格式化因子系列所选指标的格式
+        formattingData(data=[],isTrans=true){
+            return isTrans
+                ?data.map(item=>item.EdbInfoId)
+                :data
+        },
+        async addFactorIndicators(){
+            this.addFactorLoading = true
+            //因子指标系列push
+            const selectData = this.$refs.batchSelectTable.selectData
+            const {SeriesName,CalculateStep} = this.$refs.batchSelectFormula.formulaForm
+            const checkList = checkListChange(
+                this.formattingData(selectData),
+                this.formattingData(this.factorData.EdbMappings),
+            )
+            const checkSteps = checkStepsChange(
+                this.formattingStep(CalculateStep),
+                this.formattingStep(this.factorData.CalculateStep),
+            )
+            let factor = {
+                SeriesName,
+                EdbInfoType:this.factorData.EdbInfoType,
+                Calculates:this.formattingStep(CalculateStep),
+                EdbInfoIds:this.formattingData(selectData),
+                Recalculate:checkList||checkSteps,//是否需要重新计算
+            }
+            //添加 Recalculate必为true
+            //编辑 取factor.Recalculate
+            console.log('check list',checkList)
+            console.log('check steps',checkSteps)
+            const res = this.factorData.SeriesId
+                ?await chartRelevanceApi.editFactorSeries({...factor,SeriesId:this.factorData.SeriesId})
+                :await chartRelevanceApi.addFactorSeries({...factor,Recalculate:true})
+            //计算中...
+            this.addFactorLoading = false
+            if(res.Ret!==200) return 
+            //标记计算失败的指标
+            const calculatFailList = res.Data.Fail
+            const baseEdbMappings = selectData
+            //更新factorList
+            if(!this.factorData.SeriesId){
+                this.factorList.push({
+                    SeriesId:res.Data.SeriesId,//接口返的
+                    SeriesName,
+                    EdbInfoType:this.factorData.EdbInfoType,
+                    CalculateStep:CalculateStep,
+                    EdbMappings:selectData,
+                })
+            }else{
+                const index = this.factorList.findIndex(i=>i.SeriesId===this.factorData.SeriesId)
+                this.factorList.splice(index,1,{
+                    SeriesId:this.factorData.SeriesId,
+                    SeriesName,
+                    EdbInfoType:this.factorData.EdbInfoType,
+                    CalculateStep:CalculateStep,
+                    EdbMappings:selectData,
+                })
+            }
+            //如果需要重新计算 抛出事件外层组件处理
+            if(!this.factorData.SeriesId||factor.Recalculate){
+                this.$emit('checkRecalculate')
+            }
+            this.isAddFactorDialogShow = false
+        },
+        deleteFactorIndicators(index){
+            this.factorList.splice(index,1)
+            this.$emit('checkRecalculate')
+        }
+    },
+    mounted(){
+        this.factorList = this.SeriesList.map(i=>{
+            return {
+                SeriesId:i.SeriesId,
+                SeriesName:i.SeriesName,
+                CalculateStep:this.formattingStep(i.CalculateStep||[],false),
+                EdbMappings:i.EdbMappings||[],
+                EdbInfoType:i.EdbInfoType,
+            }
+        })
+    },
+};
+</script>
+
+<style lang="scss">
+.add-factor-dialog{
+    .dialog-content{
+        height: 70%;
+        overflow-y: auto;
+        .table-select-box{
+            margin-bottom: 15px;
+            display: flex;
+            align-items: center;
+            gap:0 15px;
+            .el-cascader,.el-select{
+                .el-input{
+                    width:100%;
+                }
+            }
+        }
+    }
+    .dialog-footer{
+        text-align:center;
+        margin:30px 0 20px 0;
+    }
+}
+</style>

+ 268 - 0
src/views/chartRelevance_manage/relevance/components/singleIndForm.vue

@@ -0,0 +1,268 @@
+<template>
+    <div class="single-model-form model-form">
+        <!-- 指标A -->
+        <el-form-item 
+            :label="$t('StatisticAnalysis.ChartRelevance.edbTagA')" 
+            prop="EdbInfoIdA" class="select-target">
+            <selectTarget  
+                :defaultId="chartInfoData.EdbInfoList?chartInfoData.EdbInfoList[0].EdbInfoId:''"
+                :defaultOpt="chartInfoData.EdbInfoList?[chartInfoData.EdbInfoList[0]]:[]"
+                :defaultType="chartInfoData.EdbInfoList?chartInfoData.EdbInfoList[0].EdbInfoCategoryType:''"
+                @select="(target)=>handleSelectTarget('EdbInfoIdA',target)"
+            />
+        </el-form-item>
+        <!-- 指标B -->
+        <el-form-item 
+            :label="$t('StatisticAnalysis.ChartRelevance.edbTagB')" 
+            prop="EdbInfoIdB" class="select-target">
+            <selectTarget 
+                :defaultId="chartInfoData.EdbInfoList?chartInfoData.EdbInfoList[1].EdbInfoId:''"
+                :defaultOpt="chartInfoData.EdbInfoList?[chartInfoData.EdbInfoList[1]]:[]" 
+                :defaultType="chartInfoData.EdbInfoList?chartInfoData.EdbInfoList[1].EdbInfoCategoryType:''"
+                @select="(target)=>handleSelectTarget('EdbInfoIdB',target)"
+            />
+        </el-form-item>
+        <!-- 曲线图 -->
+        <div class="form-box">
+            <div class="label-title">{{ $t('Chart.ChartType.spline_name') }}</div>
+            <!-- 时间 -->
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.time')" 
+                class="flex-form-item">
+                <!-- 时间类型 -->
+                <el-select
+                    style="width:100%"
+                    v-model="infoForm.Curve.DateType"
+                    @change="getPreviewSplineChart"
+                >
+                    <el-option
+                        v-for="item in yearSelector"
+                        :key="item.value"
+                        :label="item.name"
+                        :value="item.value"
+                    />
+                </el-select>
+                <!-- 时间段 -->
+                <date-picker
+                    v-model="infoForm.Curve.Date"
+                    v-show="infoForm.Curve.DateType===5"
+                    style="margin-left:10px;"
+                    type="month"
+                    range
+                    value-type="format"
+                    :placeholder="$t('Chart.choose_time')"
+                    @change="dateChange"
+                />
+            </el-form-item>
+            <!-- 左轴 -->
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.left_axis')" 
+                class="flex-form-item">
+                <el-input
+                    style=""
+                    :step="1"
+                    type="number"
+                    v-model="infoForm.Curve.LeftMin"
+                    @change="val => { infoForm.Curve.LeftMin=Number(val);changeSplineOption() }"
+                />
+                <span>{{ $t('StatisticAnalysis.ChartRelevance.to') }}</span>
+                <el-input
+                    style=""
+                    :step="1"
+                    type="number"
+                    v-model="infoForm.Curve.LeftMax"
+                    @change="val => { infoForm.Curve.LeftMax=Number(val);changeSplineOption() }"
+                />
+            </el-form-item>
+            <!-- 右轴 -->
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.right_axis')" 
+                class="flex-form-item">
+                <el-input
+                    style=""
+                    :step="1"
+                    type="number"
+                    v-model="infoForm.Curve.RightMin"
+                    @change="val => { infoForm.Curve.RightMin=Number(val);changeSplineOption() }"
+                />
+                <span>{{ $t('StatisticAnalysis.ChartRelevance.to') }}</span>
+                <el-input
+                    style=""
+                    :step="1"
+                    type="number"
+                    v-model="infoForm.Curve.RightMax"
+                    @change="val => { infoForm.Curve.RightMax=Number(val);changeSplineOption() }"
+                />
+            </el-form-item>
+            <!-- 指标B -->
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.edbTagB')" >
+                <el-checkbox v-model="infoForm.Curve.IsOrder"  @change="getPreviewSplineChart" style="width:100%">
+                    {{ $t('StatisticAnalysis.ChartRelevance.reverse_sequence') }}
+                </el-checkbox>
+                <div class="edb-type-box">
+                    <el-radio
+                        v-model="infoForm.Curve.EdbInfoType"
+                        :label="true"
+                        style="margin-right:10px;"
+                        @change="getPreviewSplineChart">
+                        {{ $t('StatisticAnalysis.ChartRelevance.standard_index') }}
+                    </el-radio>
+                    <el-radio
+                        v-model="infoForm.Curve.EdbInfoType"
+                        :label="false"
+                        style="margin-right:10px;"
+                        @change="getPreviewSplineChart">
+                        {{ $t('StatisticAnalysis.ChartRelevance.leading_indicator') }}
+                    </el-radio>
+                </div>
+                <div class="edb-type-lead-box" style="text-align: right;" v-if="!infoForm.Curve.EdbInfoType">
+                    <span>{{ $t('StatisticAnalysis.ChartRelevance.lead_tag') }}</span>
+                    <el-input
+                        style="width: 60px"
+                        size="mini"
+                        type="number"
+                        v-model="infoForm.Curve.LeadValue"
+                        @change="(val) => { infoForm.Curve.LeadValue = Number(val);getPreviewSplineChart()}"
+                    ></el-input>
+                    <el-select
+                        v-model="infoForm.Curve.LeadUnit"
+                        placeholder=""
+                        size="mini"
+                        style="width: 60px"
+                        @change="getPreviewSplineChart">
+                        <el-option
+                            v-for="item in dayOpt"
+                            :key="item.val"
+                            :label="item.label"
+                            :value="item.val"
+                        />
+                    </el-select>
+                </div>
+            </el-form-item>
+        </div>
+        <!-- 相关性 -->
+        <div class="form-box">
+            <div class="label-title">{{ $t('Chart.ChartType.correlation_name') }}</div>
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.calculation_window')" 
+                prop="Correlation.CalculateValue" class="flex-form-item">
+                <el-input
+                    style="flex:2"
+                    :step="1"
+                    type="number"
+                    v-model="infoForm.Correlation.CalculateValue"
+                    @change="val => { infoForm.Correlation.CalculateValue = Number(val); }"
+                />
+                <el-select
+                        style="flex:2"
+                        v-model="infoForm.Correlation.CalculateUnit"
+                >
+                    <el-option
+                        v-for="item in dayOpt"
+                        :key="item.val"
+                        :label="item.label"
+                        :value="item.val"
+                    />
+                </el-select>
+            </el-form-item>
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.analysis_cycle')" 
+                prop="Correlation.LeadValue" class="flex-form-item">
+                <el-input
+                    style="flex:2"
+                    :step="1"
+                    type="number"
+                    v-model="infoForm.Correlation.LeadValue"
+                    @change="val => { infoForm.Correlation.LeadValue = Number(val); }"
+                />
+                <el-select
+                    style="flex:2"
+                    v-model="infoForm.Correlation.LeadUnit">
+                    <el-option
+                        v-for="item in dayOpt"
+                        :key="item.val"
+                        :label="item.label"
+                        :value="item.val"
+                    />
+                </el-select>
+            </el-form-item>
+        </div>
+        <!-- 滚动相关性 -->
+        <div class="form-box" v-for="(item,index) in infoForm.RollingCorrelation" :key="index">
+            <div class="label-title">{{  $t('Chart.ChartType.rolling_correlation_name') }} {{ index+1 }}</div>
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.calculation_window')" 
+                class="flex-form-item">
+                <el-input
+                    style="flex:2"
+                    :step="1"
+                    type="number"
+                    v-model="item.CalculateValue"
+                    @change="val => { item.CalculateValue = Number(val); }"
+                />
+                <el-select
+                    style="flex:2"
+                    v-model="item.CalculateUnit">
+                    <el-option
+                        v-for="item in dayOpt"
+                        :key="item.val"
+                        :label="item.label"
+                        :value="item.val"
+                    />
+                </el-select>
+            </el-form-item>
+            <el-form-item 
+                :label="$t('StatisticAnalysis.ChartRelevance.B_leads_A')" 
+                class="flex-form-item">
+                <el-input
+                    style="flex:2"
+                    :step="1"
+                    type="number"
+                    v-model="item.LeadValue"
+                    @change="val => { item.LeadValue = Number(val); }"
+                />
+                <el-select
+                    style="flex:2"
+                    v-model="item.LeadUnit">
+                    <el-option
+                        v-for="item in dayOpt"
+                        :key="item.val"
+                        :label="item.label"
+                        :value="item.val"
+                    />
+                </el-select>
+            </el-form-item>
+        </div>
+    </div>
+</template>
+
+<script>
+import formMixin from './formMixin';
+export default {
+    mixins:[formMixin],
+    methods:{
+        dateChange(val){
+            this.infoForm.Curve.DateType = 5
+            if(val[0]) {
+                this.infoForm.Curve.StartDate = val[0];
+                this.infoForm.Curve.EndDate = val[1];
+            }else {
+                this.infoForm.Curve.StartDate = '';
+                this.infoForm.Curve.EndDate = '';
+            }
+            this.getPreviewSplineChart()
+        },
+        getPreviewSplineChart(){
+            this.$emit('previewChart')
+        },
+        changeSplineOption(){
+            this.$emit('changeSpline')
+        },
+    }
+};
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 294 - 23
src/views/chartRelevance_manage/relevance/list.vue

@@ -68,8 +68,9 @@
             :default-expanded-keys="defaultShowNodes"
             :default-expanded-keys="defaultShowNodes"
             draggable
             draggable
             :expand-on-click-node="false"
             :expand-on-click-node="false"
-            check-strictly
             empty-text="暂无分类"
             empty-text="暂无分类"
+            lazy
+            :load="getLazyTreeData"
             @node-expand="handleNodeExpand"
             @node-expand="handleNodeExpand"
             @node-collapse="handleNodeCollapse"
             @node-collapse="handleNodeCollapse"
             @current-change="nodeChange"
             @current-change="nodeChange"
@@ -95,6 +96,7 @@
                 :style="`width:${
                 :style="`width:${
                   (select_node === data.UniqueCode && node.Nodewidth) || ''
                   (select_node === data.UniqueCode && node.Nodewidth) || ''
                 }`"
                 }`"
+                :id="`node${data.UniqueCode}`"
               >
               >
                 <span>{{ currentLang==='en' ? (data.ChartClassifyNameEn||data.ChartClassifyName) : data.ChartClassifyName  }}</span>
                 <span>{{ currentLang==='en' ? (data.ChartClassifyNameEn||data.ChartClassifyName) : data.ChartClassifyName  }}</span>
               </span>
               </span>
@@ -106,6 +108,15 @@
                   src="~@/assets/img/data_m/move_ico.png"
                   src="~@/assets/img/data_m/move_ico.png"
                   alt=""
                   alt=""
                   style="width: 14px; height: 14px; margin-right: 8px"
                   style="width: 14px; height: 14px; margin-right: 8px"
+                  v-if="permissionBtn.isShowBtn('statisticPermission','corrAnalysis_classifyOpt_move')"
+                />
+                <!-- 添加子项 -->
+                <img
+                    src="~@/assets/img/set_m/add.png"
+                    alt=""
+                    style="width: 14px; height: 14px; margin-right: 8px"
+                    @click.stop="addNode(node,data)"
+                    v-if="data.Button.AddButton&&node.level<6"
                 />
                 />
                 <img
                 <img
                   src="~@/assets/img/set_m/edit.png"
                   src="~@/assets/img/set_m/edit.png"
@@ -167,6 +178,9 @@
                     ref="chartRef"
                     ref="chartRef"
                   />
                   />
                 </div>
                 </div>
+                <span class="chart-source" v-if="chartInfo.SourcesFrom.isShow&&chartInfo.SourcesFrom.text">
+                    数据来源:{{ chartInfo.SourcesFrom.text }}
+                </span>
                 <span class="chart-author"
                 <span class="chart-author"
                   >{{$t('MsgPrompt.author')}}:{{ chartInfo.SysUserRealName }}</span
                   >{{$t('MsgPrompt.author')}}:{{ chartInfo.SysUserRealName }}</span
                 >
                 >
@@ -262,7 +276,7 @@
           :total="chart_total" 
           :total="chart_total" 
           :list="chartList" 
           :list="chartList" 
           @loadMoreHandle="loadMoreHandle"
           @loadMoreHandle="loadMoreHandle"
-          @detailShowHandle="detailShowHandle"
+          @detailShowHandle="handleShowChartDetail"
           @addMychartHandle="addMychartHandle"
           @addMychartHandle="addMychartHandle"
           ref="chartListWrap"
           ref="chartListWrap"
         />
         />
@@ -270,11 +284,18 @@
     </div>
     </div>
 
 
     <!-- 分类弹窗 -->
     <!-- 分类弹窗 -->
-    <classify-dia
+    <!-- <classify-dia
       :isOpenDialog.sync="classifyDia"
       :isOpenDialog.sync="classifyDia"
       :title="dialog_title"
       :title="dialog_title"
       :form="classifyForm"
       :form="classifyForm"
       @successCallback="getTreeData"
       @successCallback="getTreeData"
+    /> -->
+    <modifyClassifyDialog
+      :isOpenDialog.sync="classifyDia"
+      :type="classifyDiaType"
+      :formData="classifyForm"
+      @sucessCallback="sucessCallback"
+      @closeDia="classifyDia = false"
     />
     />
 
 
     <!-- 加入我的图库弹窗 -->
     <!-- 加入我的图库弹窗 -->
@@ -319,12 +340,14 @@ import leftMixin from "../mixins/classifyMixin";
 import Chart from "@/views/dataEntry_manage/components/chart";
 import Chart from "@/views/dataEntry_manage/components/chart";
 import changeLang from "@/views/dataEntry_manage/components/changeLang.vue";
 import changeLang from "@/views/dataEntry_manage/components/changeLang.vue";
 import classifyDia from "@/views/datasheet_manage/components/sheetClassifyDia.vue";
 import classifyDia from "@/views/datasheet_manage/components/sheetClassifyDia.vue";
+import modifyClassifyDialog from './components/modifyClassifyDialog.vue'
 import addMyClassifyDia from "@/views/dataEntry_manage/components/addMyClassifyDia";
 import addMyClassifyDia from "@/views/dataEntry_manage/components/addMyClassifyDia";
 import SaveChartOther from "@/views/dataEntry_manage/components/SaveChartOther";
 import SaveChartOther from "@/views/dataEntry_manage/components/SaveChartOther";
 import setEnNameDia from "@/views/dataEntry_manage/components/setEnNameDia.vue";
 import setEnNameDia from "@/views/dataEntry_manage/components/setEnNameDia.vue";
 import { chartSetMixin } from "@/views/dataEntry_manage/mixins/chartPublic";
 import { chartSetMixin } from "@/views/dataEntry_manage/mixins/chartPublic";
 import { copyOtherOptions } from '@/utils/defaultOptions';
 import { copyOtherOptions } from '@/utils/defaultOptions';
 import setLangInfoDia from '@/views/dataEntry_manage/components/setLangInfo.vue'
 import setLangInfoDia from '@/views/dataEntry_manage/components/setLangInfo.vue'
+import { baseSourcesFrom } from './utils/config'
 export default {
 export default {
   components: {
   components: {
     changeLang,
     changeLang,
@@ -333,7 +356,8 @@ export default {
     addMyClassifyDia,
     addMyClassifyDia,
     SaveChartOther,
     SaveChartOther,
     setEnNameDia,
     setEnNameDia,
-    setLangInfoDia
+    setLangInfoDia,
+    modifyClassifyDialog
   },
   },
   mixins: [leftMixin, chartSetMixin],
   mixins: [leftMixin, chartSetMixin],
   computed: {
   computed: {
@@ -359,11 +383,12 @@ export default {
       defaultProp: {
       defaultProp: {
         label: "ChartClassifyName",
         label: "ChartClassifyName",
         children: "Children",
         children: "Children",
+        isLeaf:'isLeaf'
       }, //树结构配置项
       }, //树结构配置项
       dynamicNode: null,
       dynamicNode: null,
 
 
       /* 分类弹窗 */
       /* 分类弹窗 */
-      dialog_title: "",
+      classifyDiaType: "add",//add or edit
       classifyDia: false, //
       classifyDia: false, //
       classifyForm: {},
       classifyForm: {},
 
 
@@ -392,10 +417,196 @@ export default {
     };
     };
   },
   },
   methods: {
   methods: {
+    /* 判断节点是否能被拖拽 */
+    canDragHandle({data}) {
+        return data.Button.MoveButton&&this.permissionBtn.isShowBtn('statisticPermission','corrAnalysis_classifyOpt_move');
+    },
+    /* 判断节点是否能被拖入 */
+    canDropHandle(draggingNode, dropNode, type) {
+        let canDrop=false
+        
+        // 如果拖动的是图表
+        if(draggingNode.data.ChartInfoId){
+            if(!(dropNode.level===1&&type!=='inner')){
+                canDrop=true
+            }
+        }else{//拖动的是目录
+            //目录层级不能改变
+            if((dropNode.level+1==draggingNode.level&&type==='inner'&&!dropNode.data.ChartInfoId)||(dropNode.level===draggingNode.level&&type!=='inner')){
+                canDrop=true
+            }
+        }
+        return canDrop
+    },
+    /* 拖拽完成 */
+    dropOverHandle(b,a,i,e) {
+        // 被拖拽节点对应的 Node、结束拖拽时最后进入的节点、被拖拽节点的放置位置
+        const isChart=b.data.ChartInfoId?true:false
+        let list=a.parent.childNodes;
+        let targetIndex=0
+        let ClassifyId=0,//分类ID,若当前节点为图表,则为0
+        ParentClassifyId=0,//移动后,所在位置的父级分类ID
+        PrevClassifyId=0,//前一个节点的分类ID,若前一个节点为图表,则为0
+        NextClassifyId=0,//后一个节点的分类ID,同上
+        ChartInfoId=0,//图表ID,若当前节点为分类,则为0
+        PrevChartInfoId=0,//前一个节点的图表ID,若前一个节点为分类,则为0
+        NextChartInfoId=0;//后一个节点的图表ID
+
+        ClassifyId=isChart?0:b.data.ChartClassifyId
+        ChartInfoId=isChart?b.data.ChartInfoId:0
+        
+
+        if(i!=='inner'){
+            ParentClassifyId=a.parent.data.ChartClassifyId||0
+            list.forEach((item,index)=>{
+                if(isChart){
+                    if(item.data.ChartInfoId===b.data.ChartInfoId){
+                        targetIndex=index
+                    }
+                }else{
+                    if(item.data.ChartClassifyId===b.data.ChartClassifyId){
+                        targetIndex=index
+                    }
+                }
+            })
+
+            console.log(targetIndex);
+            
+            
+            if(targetIndex===0){
+                const data=list[targetIndex+1].data
+                NextClassifyId=data.ChartInfoId?0:data.ChartClassifyId
+                NextChartInfoId=data.ChartInfoId?data.ChartInfoId:0
+            }else if(targetIndex===list.length-1){
+                const data=list[targetIndex-1].data
+                PrevClassifyId=data.ChartInfoId?0:data.ChartClassifyId
+                PrevChartInfoId=data.ChartInfoId?data.ChartInfoId:0
+            }else{
+                const pData=list[targetIndex-1].data
+                PrevClassifyId=pData.ChartInfoId?0:pData.ChartClassifyId
+                PrevChartInfoId=pData.ChartInfoId?pData.ChartInfoId:0
+
+                const nData=list[targetIndex+1].data
+                NextClassifyId=nData.ChartInfoId?0:nData.ChartClassifyId
+                NextChartInfoId=nData.ChartInfoId?nData.ChartInfoId:0
+            }
+        }else{
+            ParentClassifyId=a.data.ChartClassifyId||0
+        }
+
+        const params={
+            ClassifyId,
+            ParentClassifyId,
+            PrevClassifyId,
+            NextClassifyId,
+            ChartInfoId,
+            PrevChartInfoId,
+            NextChartInfoId
+        }
+        console.log(params);
+        chartRelevanceApi.classifyMove(params).then(res=>{
+            if(res.Ret===200){
+                // this.$message.success('移动成功!')
+                this.$message.success(this.$t('MsgPrompt.move_sort_success'))
+            }
+            this.getTreeData()
+            if(this.select_id){
+                this.getDetailHandle();
+            }
+            
+        })
+    },
+
+    /* 拖拽覆盖添加背景色 */
+    dropMouseOver(node1,node2,e) {
+        // console.log(e.layerY);
+        
+        // 被拖拽节点对应的 Node、所进入节点对应的 Node、event
+        if(!node2.data.EdbInfoId&&(node1.level>node2.level||(node1.data.EdbInfoId>0&&!node2.data.EdbInfoId)) && (e.target.childNodes[0].className.includes('el-tree-node__content') 
+        || e.target.className.includes('el-tree-node__content'))) {
+            // console.log(e.target.childNodes[0])
+            e.target.childNodes[0].className.includes('el-tree-node__content') 
+            ? e.target.childNodes[0].style.backgroundColor = '#409eff' 
+            : e.target.style.backgroundColor = '#409eff';
+        }
+        
+    },
+    /* 拖拽离开/拖拽完成重置背景色 */
+    dropMouseLeave(node1,node2,e) {
+        let arrs = $('.el-tree-node__content');
+        for( let a of arrs ) {
+            a.style.backgroundColor = 'transparent';
+        }
+    },
+    sucessCallback(type){
+        this.classifyDia = false;
+        this.getTreeData();
+        if(type === 'add') {
+            //新增分类完成之后,展开父节点显示刚新增的分类,若已展开节点则不做处理
+            let code = sessionStorage.getItem('expandCode');
+            let flag = 	this.defaultShowNodes.some((item) => {
+                return item === code
+            });
+            !flag &&code&& this.defaultShowNodes.push(code);
+            sessionStorage.removeItem('expandCode');
+        }
+    },
+    // 递归节点
+    getNodeParentData(data,arr){
+        if(data.level===0) return
+        arr.push({classifyName:this.currentLang==='en'?data.data.ChartClassifyNameEn:data.data.ChartClassifyName,classifyId:data.data.ChartClassifyId})
+        this.getNodeParentData(data.parent,arr)
+        return arr
+    },
+    addNode(node,data){
+        this.classifyDiaType = 'add';
+        let arr=[]
+        arr=this.getNodeParentData(node,arr)
+        /* 添加目录 */
+        this.classifyForm = {
+            parentArr:arr,
+            parent_id: data.ChartClassifyId,
+            level: node.level,
+            levelVal:'',
+            nodeId:'',
+        }
+        //存储当前要新增子级的目录code
+        sessionStorage.setItem('expandCode', data.UniqueCode);
+        this.classifyDia = true;
+    },
+    //绑定el-tree的load属性
+    async getLazyTreeData (node,resolve){
+        if(node.level===0){
+            resolve(this.treeData)
+        }else{
+            let arr=[]
+            const res=await chartRelevanceApi.classifyList({
+                ParentId:node.data.ChartClassifyId,
+                IsShowMe:this.isOnlyMe,
+                Source: this.classify_tab ? 4 : 3})
+            if (res.Ret === 200) {
+                const temarr = res.Data.AllNodes || [];
+                arr=temarr.map(item=>{
+                    return {
+                        ...item,
+                        isLeaf:item.ChartInfoId?true:false
+                    }
+                })
+            }
+            resolve(arr)
+        }
+    },
+    //展开选中图表父级,选中选择图表
+    handleShowChartDetail({ UniqueCode, ChartInfoId }){
+        this.select_classify = 0;
+        this.select_id = ChartInfoId;
+        this.select_node = UniqueCode;
+    },
     /* 添加图表 */
     /* 添加图表 */
     goAddChart() {
     goAddChart() {
       if (!this.treeData.length) return this.$message.warning("请先添加分类");
       if (!this.treeData.length) return this.$message.warning("请先添加分类");
-      this.$router.push({ path: "/relevancechartEditor" });
+      //this.$router.push({ path: "/relevancechartEditor" });
+      this.$router.push({ path: "/relevancechartEditorV2" });
     },
     },
 
 
     /* 获取分类 */
     /* 获取分类 */
@@ -410,10 +621,20 @@ export default {
           if (Ret !== 200) return;
           if (Ret !== 200) return;
 
 
           this.showData = true;
           this.showData = true;
-          this.treeData = Data.AllNodes || [];
+          //this.treeData = Data.AllNodes || [];
+          this.treeData = Data.AllNodes?Data.AllNodes.map(d=>{
+            return {
+                ...d,
+                Children:[]
+            }
+          }):[]
           this.$nextTick(() => {
           this.$nextTick(() => {
             /* 新增完成后 处理树展开和选中 */
             /* 新增完成后 处理树展开和选中 */
-            params && this.selectCurrentNode(params);
+            //params && this.selectCurrentNode(params);
+            if(params){
+                this.select_node = params.code;
+                this.select_id = params.id;
+            }
           });
           });
         });
         });
     },
     },
@@ -471,29 +692,33 @@ export default {
       this.search_txt = "";
       this.search_txt = "";
       this.select_node = UniqueCode;
       this.select_node = UniqueCode;
       this.select_classify = !ChartInfoId ? ChartClassifyId : 0;
       this.select_classify = !ChartInfoId ? ChartClassifyId : 0;
-      // if(this.select_id !== ChartInfoId) {
       this.select_id = ChartInfoId || 0;
       this.select_id = ChartInfoId || 0;
-      // }
       this.resetNodeStyle(node);
       this.resetNodeStyle(node);
       this.dynamicNode = node;
       this.dynamicNode = node;
     },
     },
 
 
     /* 添加一级目录 */
     /* 添加一级目录 */
     addLevelOneHandle() {
     addLevelOneHandle() {
-      this.dialog_title = this.$t('StatisticAnalysis.ChartRelevance.add_chart_classify')||"添加图表分类";
-      this.classifyForm = {
-        classify_name: "",
-      };
-      this.classifyDia = true;
+        this.classifyDiaType = 'add';
+        this.classifyForm = {
+            parent_id: 0,
+            level: 0,
+            levelVal:'',
+        };
+        this.classifyDia = true;
     },
     },
 
 
     /* 编辑节点 */
     /* 编辑节点 */
     editNode(node, { ChartClassifyName,ChartClassifyNameEn, ChartClassifyId }) {
     editNode(node, { ChartClassifyName,ChartClassifyNameEn, ChartClassifyId }) {
-      this.dialog_title = this.$t('StatisticAnalysis.ChartRelevance.edit_chart_classify')||"编辑图表分类";
+      this.classifyDiaType = 'edit'
+      let arr=[]
+      arr=this.getNodeParentData(node.parent,arr)
       /* 编辑目录 */
       /* 编辑目录 */
       this.classifyForm = {
       this.classifyForm = {
-        classify_name: this.currentLang==='en'?ChartClassifyNameEn:ChartClassifyName,
-        classify_id: ChartClassifyId,
+        parentArr:arr,
+        level:node.level,
+        levelVal: this.currentLang==='en'?ChartClassifyNameEn:ChartClassifyName,
+        nodeId: ChartClassifyId,
       };
       };
       this.classifyDia = true;
       this.classifyDia = true;
     },
     },
@@ -520,7 +745,13 @@ export default {
           }).then(() => {
           }).then(() => {
             this.delApi(ChartClassifyId, ChartInfoId);
             this.delApi(ChartClassifyId, ChartInfoId);
           })
           })
-        : null;
+        : DeleteStatus===2
+        ? this.$confirm(this.$t('Edb.MsgPrompt.del_confirm_menu_or_children'),this.$t('Confirm.prompt'),{
+            type:"warning",
+        }).then(()=>{
+            this.delApi(ChartClassifyId, ChartInfoId);
+        })
+        :null;
     },
     },
 
 
     /* 删除方法 */
     /* 删除方法 */
@@ -618,10 +849,21 @@ export default {
         })
         })
         .then((res) => {
         .then((res) => {
           if (res.Ret !== 200) return;
           if (res.Ret !== 200) return;
-
           this.chartInfo = res.Data.ChartInfo;
           this.chartInfo = res.Data.ChartInfo;
           this.tableData = res.Data.EdbInfoList;
           this.tableData = res.Data.EdbInfoList;
+          this.defaultShowNodes = res.Data.ClassifyLevels||[];
+          this.$nextTick(()=>{
+            setTimeout(() => {
+                this.$refs.treeRef.setCurrentKey(this.select_node);
+            }, 1200);
+          })
           if(this.chartInfo.Source === 3) {
           if(this.chartInfo.Source === 3) {
+            const SourcesFrom = this.chartInfo.SourcesFrom
+            try{
+                this.chartInfo.SourcesFrom = SourcesFrom?JSON.parse(SourcesFrom):baseSourcesFrom
+            }catch(e){
+                this.chartInfo.SourcesFrom = baseSourcesFrom
+            }
             this.relevanceChartData = {
             this.relevanceChartData = {
               ChartInfo: res.Data.ChartInfo,
               ChartInfo: res.Data.ChartInfo,
               EdbInfoList: res.Data.EdbInfoList,
               EdbInfoList: res.Data.EdbInfoList,
@@ -630,12 +872,16 @@ export default {
               YDataList: [
               YDataList: [
                 {
                 {
                   Value: res.Data.YDataList[0].Value,
                   Value: res.Data.YDataList[0].Value,
-                  Color: "#00f",
+                  Color: res.Data.YDataList[0].Color,
                   Name: res.Data.ChartInfo.ChartName,
                   Name: res.Data.ChartInfo.ChartName,
                   NameEn: res.Data.ChartInfo.ChartNameEn
                   NameEn: res.Data.ChartInfo.ChartNameEn
                 },
                 },
               ],
               ],
             };
             };
+            //多因子
+            if(res.Data.CorrelationChartInfo.AnalysisMode===1){
+                this.relevanceChartData.YDataList = res.Data.YDataList
+            }
             this.initRelevanceChartData() 
             this.initRelevanceChartData() 
           }else if(this.chartInfo.Source === 4) { //滚动相关性逻辑又换成曲线了
           }else if(this.chartInfo.Source === 4) { //滚动相关性逻辑又换成曲线了
             this.relevanceChartData = {
             this.relevanceChartData = {
@@ -643,7 +889,25 @@ export default {
             }
             }
             this.setDefaultChart([res.Data.DataResp]);
             this.setDefaultChart([res.Data.DataResp]);
           }
           }
-          
+          this.$nextTick(()=>{
+                const _node = this.$refs.treeRef.getNode(this.select_node)
+                this.dynamicNode = _node;
+                this.dynamicNode&&this.resetNodeStyle(this.dynamicNode)
+                setTimeout(() => {
+                    let node = document.getElementById(`node${this.select_node}`)||{}
+                    let parent = document.getElementsByClassName('tree-cont')[0];
+                    //parent可视区间:[scrollTop,scrollTop+offsetHeight]
+                    //node位置:node.offsetTop
+                    const overTop = node.offsetTop+node.clientHeight+30<parent.scrollTop
+                    const overBottom = node.offsetTop+node.clientHeight+30>parent.scrollTop+parent.offsetHeight
+                    if(overTop){
+                        parent.scrollTop = node.offsetTop-60
+                    }
+                    if(overBottom){
+                        parent.scrollTop =  node.offsetTop - parent.offsetHeight/2
+                    }
+                },1500)
+            })
         });
         });
     },
     },
 
 
@@ -685,11 +949,18 @@ export default {
 
 
     /* 编辑图表 */
     /* 编辑图表 */
     editChartHandle() {
     editChartHandle() {
-      this.$router.push({
+      /* this.$router.push({
         path: "/relevancechartEditor",
         path: "/relevancechartEditor",
         query: {
         query: {
           code: this.chartInfo.UniqueCode,
           code: this.chartInfo.UniqueCode,
         },
         },
+      }); */
+      this.$router.push({
+        path: "/relevancechartEditorV2",
+        query: {
+          code: this.chartInfo.UniqueCode,
+          type:this.relevanceChartData.CorrelationChartInfo.AnalysisMode
+        },
       });
       });
     },
     },
 
 

+ 1089 - 0
src/views/chartRelevance_manage/relevance/relevanceChartEditorV2.vue

@@ -0,0 +1,1089 @@
+<template>
+    <div class="relevance-chart-editor-wrap" v-loading="previewMatrixLoading" element-loading-text="正在计算相关性矩阵...">
+        <div class="info-wrap content-wrap">
+            <div class="info-top">
+                <el-button type="primary" @click="previewChart">{{$t('Dialog.calculate_btn')}}</el-button>
+                <el-button type="primary" plain @click="$router.back()">{{$t('Dialog.cancel_btn')}}</el-button>
+                <!-- 操作说明 -->
+                <span @click="showExplain = true">
+                    <img style="width:15px;height:15px;" src="~@/assets/img/icons/formula-add.png" alt="">
+                    {{$t('StatisticAnalysis.ChartRelevance.opt_tip_btn')}}
+                </span>
+            </div>
+            <div class="info-form-wrap">
+                <el-form :model="infoForm" :rules="infoRules" label-width="80px" ref="infoFormRef">
+                    <!-- 分析模式 -->
+                    <el-form-item 
+                        :label="$t('StatisticAnalysis.ChartRelevance.analytical_model')" 
+                        prop="Model" required>
+                        <el-select v-model="infoForm.Model" style="width: 100%;" 
+                        :disabled="$route.query.code||isMultipleChartAdd||buttonAuth.isRelevanceChartAdd"
+                        @change="changeModel">
+                            <el-option :label="$t('StatisticAnalysis.ChartRelevance.single_indicator')" :value="1"></el-option>
+                            <el-option :label="$t('StatisticAnalysis.ChartRelevance.multiple_indicators')" :value="2"></el-option>
+                        </el-select>
+                    </el-form-item>
+                    <!-- 单因子模式需要填的 -->
+                    <singleIndForm
+                        v-if="infoForm.Model===1"
+                        :info-form="infoForm"
+                        :chart-info-data="chartInfoData"
+                        @selectTarget="handleSelectTarget"
+                        @previewChart="getPreviewSplineChart"
+                        @changeSpline="changeSplineOption"
+                    ></singleIndForm>
+                    <!-- 多因子模式需要填的 -->
+                    <multipleIndForm
+                        v-if="infoForm.Model===2"
+                        ref="multipleIndForm"
+                        :info-form="infoForm"
+                        :isMultipleChartAdd="isMultipleChartAdd"
+                        :chart-info-data="chartInfoData"
+                        :SeriesList="SeriesList"
+                        @selectTarget="handleSelectTarget"
+                        @checkRecalculate="checkRecalculate"
+                    ></multipleIndForm>
+                </el-form>
+            </div>
+        </div>
+        <div class="model-wrap">
+            <!-- 单因子模式 -->
+            <div class="single-model-wrap" v-if="infoForm.Model===1">
+                <div
+                    class="chart-min-cont"
+                    v-if="chartBatchData"
+                >
+                    <!-- 曲线图 -->
+                    <div class="card-wrapper content-wrap">
+                        <chartCard 
+                            :entryType="1"
+                            ref="chartCard1"
+                            :data="chartBatchData.CurveData"
+                            :settings="infoForm"
+                            :isChartAdd="buttonAuth.isCurveChartAdd"
+                            @saveChart="(params)=>{handleSave(params);isSaveChartToBase = true;}"
+                            @saveEdb="(params)=>{handleSave(params);isSaveEdbToBase = true;}"
+                        />
+                    </div>
+                    <!-- 相关性图表 -->
+                    <div class="card-wrapper content-wrap" v-if="chartBatchData.CorrelationData">
+                        <chartCard 
+                            :entryType="2"
+                            ref="chartCard2"
+                            :data="chartBatchData.CorrelationData"
+                            :settings="infoForm"
+                            :isChartAdd="buttonAuth.isRelevanceChartAdd"
+                            :isChartSetting="true"
+                            @chartSettingChange="handleChangeChartSetting"
+                            @saveChart="(params)=>{handleSave(params);isSaveChartToBase = true;}"
+                            @saveEdb="(params)=>{handleSave(params);isSaveEdbToBase = true;}"
+                        />
+                        <!-- 图表数据来源 -->
+                        <div class="source" v-if="chartBatchData.SourcesFrom.text&&chartBatchData.SourcesFrom.isShow">
+                            {{ $t('Edb.Detail.source') }}:{{ chartBatchData.SourcesFrom.text }}
+                        </div>
+                    </div>
+                    <!-- 滚动相关性1 -->
+                    <template v-if="chartBatchData.RollingCorrelationData">
+                        <div class="card-wrapper content-wrap" v-if="chartBatchData.RollingCorrelationData[0]">
+                            <chartCard 
+                                :entryType="3"
+                                ref="chartCard3"
+                                :data="chartBatchData.RollingCorrelationData[0]"
+                                :settings="infoForm"
+                                :isChartAdd="buttonAuth.isRollChartAdd"
+                                :isEdbAdd="buttonAuth.isRollEdbAdd"
+                                @saveChart="(params)=>{handleSave(params);isSaveChartToBase = true;}"
+                                @saveEdb="(params)=>{handleSave(params);isSaveEdbToBase = true;}"
+                            />
+                        </div>
+                        <!-- 滚动相关性2 -->
+                        <div class="card-wrapper content-wrap" v-if="chartBatchData.RollingCorrelationData[1]">
+                            <chartCard 
+                                :entryType="4"
+                                ref="chartCard4"
+                                :data="chartBatchData.RollingCorrelationData[1]"
+                                :settings="infoForm"
+                                :isChartAdd="buttonAuth.isRollChartTwoAdd"
+                                :isEdbAdd="buttonAuth.isRollEdbTwoAdd"
+                                @saveChart="(params)=>{handleSave(params);isSaveChartToBase = true;}"
+                                @saveEdb="(params)=>{handleSave(params);isSaveEdbToBase = true;}"
+                            />
+                        </div>
+                    </template>
+                </div>
+
+                <div class="nodata" v-else>
+                    <tableNoData text="暂无信息"/>
+                </div>
+            </div>
+            <!-- 多因子模式 -->
+            <div class="multiple-model-wrap" v-if="infoForm.Model===2">
+                <!-- 相关性矩阵表格 -->
+                <div class="relevant-matrix-table-box" v-if="factorTableData.length">
+                    <el-table style="width: 100%;" height="100%" :data="factorTableData" :row-class-name="getTableRowClassname" @sort-change="sortChange">
+                        <el-table-column 
+                            fixed
+                            :label="$t('StatisticAnalysis.ChartRelevance.multiple_table_head_01')"
+                            width="220px"
+                            align="center"
+                            class-name="zip-cell"
+                            >
+                            <el-table-column
+                                width="80px" align="center"
+                                class-name="zip-cell"
+                                :label="$t('Edb.Detail.e_opera')">
+                               <template slot-scope="{row}">
+                                    <template v-if="row.isSuccess">
+                                        <span style="color:#0052D9;cursor: pointer;" @click="addCurve(row)" v-if="!row.isAdd">{{$t('StatisticAnalysis.ChartRelevance.multiple_table_btn_add')}}</span>
+                                        <span style="color:#D54941;cursor: pointer;" @click="deleteCurve(row)" v-else>{{$t('StatisticAnalysis.ChartRelevance.multiple_table_btn_del')}}</span>
+                                    </template>
+                               </template>
+                            </el-table-column>
+                            <el-table-column
+                                width="140px" align="center"
+                                class-name="zip-cell"
+                                show-overflow-tooltip
+                                prop="EdbName"
+                                :label="$t('StatisticAnalysis.ChartRelevance.multiple_table_head_02')">
+                            </el-table-column>
+                        </el-table-column>
+                        <!-- 领先天数 -->
+                        <el-table-column
+                            v-if="SortedArray.length"
+                            class-name="zip-cell"
+                            :label="LeadLabelName">
+                            <el-table-column v-for="(num,index) in SortedArray" :key="num"
+                                min-width="45px"
+                                sortable="custom"
+                                prop="LeadValue"
+                                align="center"
+                                class-name="zip-cell"
+                                :label="num">
+                                <template slot-scope="{row}">
+                                    {{ row.dataList[index] }}
+                                </template>
+                            </el-table-column>
+                        </el-table-column>
+                    </el-table>
+                </div>
+                <!-- 相关性图表 -->
+                <div class="relevant-chart-box content-wrap" v-if="showMultipleChart">
+                    <chartCard
+                        ref="chartCard2"
+                        :data="multipleChartData"
+                        :settings="infoForm"
+                        :entryType="2"
+                        :isChartSetting="true"
+                        :isChartAdd="isMultipleChartAdd"
+                        height="300"
+                        @handleEdit="saveMultipleChart"
+                        @saveChart="(params)=>{handleSave(params);isSaveChartToBase = true;}"
+                        @saveEdb="(params)=>{handleSave(params);isSaveEdbToBase = true;}"
+                        @chartSettingChange="handleChangeChartSetting"
+                    />
+                    <!-- 数据来源 -->
+                    <div class="source" v-if="multipleChartData.SourcesFrom.text&&multipleChartData.SourcesFrom.isShow">
+                        {{ $t('Edb.Detail.source') }}:{{ multipleChartData.SourcesFrom.text }}
+                    </div>
+                </div>
+
+                <div class="nodata" v-if="!factorTableData.length">
+                    <tableNoData :text="$t('Common.no_info_msg')"/>
+                </div>
+            </div>
+        </div>
+        <!-- 图表保存/另存为 -->
+        <saveChartToBase
+            :isShow.sync="isSaveChartToBase"
+            :source="saveSource"
+            :saveScence="saveScence"
+            :chartData="chartData"
+            @saveBack="saveChartBack"
+            @handleSave="saveMultipleChart"
+        />
+
+        <!-- 指标保存/另存为 -->
+        <saveEdbToBase
+            :isShow.sync="isSaveEdbToBase"
+            :source="saveSource"
+            :saveScence="saveScence"
+            :chartData="chartData"
+            @saveBack="saveEdbBack"
+        />
+        <!-- 图例设置 -->
+        <saveChartSetting
+            :isSettingChartShow="isSettingChartShow"
+            :settingData="settingData"
+            @saveChartSetting="changeChartSettingBack"
+            @close="isSettingChartShow = false"
+        />
+        <!-- 操作说明 -->
+        <ExplainDialog 
+            :show-explain="showExplain"
+            @close="showExplain = false"
+        />
+    </div>
+</template>
+
+<script>
+/* api */
+import { dataBaseInterface } from '@/api/api.js'
+import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js'
+import chartRelevanceApi from '@/api/modules/chartRelevanceApi'
+import {etaBaseConfigInterence} from '@/api/modules/etaBaseConfigApi.js';
+
+/*components */
+import selectTarget from '../components/selectTarget.vue'
+import singleIndForm from './components/singleIndForm.vue'
+import multipleIndForm from './components/multipleIndForm.vue'
+import chartCard from '../components/chartCard.vue'
+import saveChartToBase from '../components/saveChartTobaseDia.vue'
+import saveEdbToBase from '../components/saveEdbToBaseDia.vue'
+import saveChartSetting from './../components/saveChartSetting.vue'
+import ExplainDialog from '../components/explainDialog.vue'
+/* utils */
+import {generateSortedArray,generateXEdbValue,generateYDataValue} from './utils/index'
+import { baseForm, baseChartInfo,baseSourcesFrom } from './utils/config'
+export default {
+    components:{
+        selectTarget, singleIndForm, multipleIndForm,
+        chartCard,saveChartToBase,saveEdbToBase,saveChartSetting,
+        ExplainDialog
+    },
+    data() {
+        return {
+            typeModel:0,
+            infoForm:_.cloneDeep(baseForm),
+            chartInfoData: {},//单因子指标信息,新增时为空,编辑时有值,用于回显selectTarget,多因子取第一项
+            chartBatchData:null,//单因子图表信息
+            buttonAuth: { //单因子图表按钮控制
+                isCurveChartAdd: false,
+                isRelevanceChartAdd: false,
+                isRollChartAdd: false,
+                isRollEdbAdd: false,
+                isRollChartTwoAdd: false,
+                isRollEdbTwoAdd: false,
+            },
+            isMultipleChartAdd:false,//多因子图表按钮控制
+            SeriesList:[],//多因子系列列表,编辑时有值
+            SortedArray:[],//多因子-领先期数数组 ±N
+            factorTableData:[],//多因子-相关性矩阵表格
+            factorTableDataSortCopy:[],//复制一份,用于还原排序
+            multipleChartData:_.cloneDeep(baseChartInfo),//多因子图表信息
+            IndTarget:{},//存储标的指标信息
+            showMultipleChart:false,//是否显示多因子相关性图表
+
+            isSaveChartToBase:false,
+            isSaveEdbToBase:false,
+            saveScence:'',
+            saveSource:0,
+            chartData:'',
+
+            settingData:{SourcesFrom:{}},//图例信息
+            isSettingChartShow:false,
+
+            showExplain:false,//显示操作说明
+            SourcesFromVisable:false,//数据来源的默认值,仅在新增时生效
+            previewMatrixLoading:false,//计算相关性矩阵loading
+        };
+    },
+    computed:{
+        //领先期数label
+        LeadLabelName(){
+            const UnitEnMap = {
+                    '年': 'Year',
+                    '季': 'Season',
+                    '月': 'Month',
+                    '周': 'Week',
+                    '天': 'Day',
+                }
+            return this.$t('StatisticAnalysis.ChartRelevance.multiple_table_head_03',{unit:UnitEnMap[this.infoForm.Correlation.LeadUnit]})
+        },
+        infoRules(){
+            return {
+                EdbInfoIdA:[{
+                    required:true,
+                    message:/* '指标A未选择' */this.$t('StatisticAnalysis.ChartRelevance.infoform_rules_hint_01'),
+                    trigger:'blur'
+                }],
+                EdbInfoIdB:[{
+                    required:true,
+                    message:/* '指标B未选择' */this.$t('StatisticAnalysis.ChartRelevance.infoform_rules_hint_02'),
+                    trigger:'blur'
+                }],
+                IndTarget:[{
+                    required:true,
+                    message:/* '标的指标未选择' */this.$t('StatisticAnalysis.ChartRelevance.infoform_rules_hint_03'),
+                    trigger:'blur'
+                }],
+                'Correlation.CalculateValue':[{
+                    required:true,
+                    message:/* '计算窗口未填写' */this.$t('StatisticAnalysis.ChartRelevance.infoform_rules_hint_04'),
+                    trigger:'blur'}],
+                'Correlation.LeadValue':[{
+                    required:true,
+                    message:/* '分析周期未填写' */this.$t('StatisticAnalysis.ChartRelevance.infoform_rules_hint_05'),
+                    trigger:'blur'
+                }]
+            }
+        },
+        chartInfo(){ //单因子系列保存/更新/另存为会用到
+            return this.infoForm
+        }
+    },
+    mounted(){
+        this.getRelevanceChartDetail()
+        this.getChartBaseSetting()
+    },
+    methods: {
+        //获取图表全局设置
+        async getChartBaseSetting(){
+            //目前是用基本配置的接口,后续有多个配置再改
+            const res = await etaBaseConfigInterence.getBaseConfig()
+            if(res.Ret!==200) return 
+            const {ChartSourceDisplay} = res.Data||{}
+            this.SourcesFromVisable = ChartSourceDisplay==='true'?true:false
+            //多因子的配置一开始就初始化,在这里设置默认值
+            if(!this.$route.query.code){
+                this.multipleChartData.SourcesFrom.isShow = this.SourcesFromVisable
+            }
+        },
+        sortChange({column,prop,order}){
+            const {label} = column
+            this.factorTableData.sort((a,b)=>{
+                if(order==='ascending'){
+                    return a.dataList[label] - b.dataList[label]
+                }else{
+                    return b.dataList[label] - a.dataList[label]
+                }
+            })
+            //取消order 排序还原为factorTableDataSortCopy的顺序
+            if(!order){
+                const table = this.factorTableDataSortCopy.map(copyItem=>{
+                    return this.factorTableData.find(i=>i.EdbInfoId===copyItem.EdbInfoId&&i.SeriesId===copyItem.SeriesId)
+                })
+                this.factorTableData = table
+            }
+            
+
+        },
+        //禁用表格行的样式
+        getTableRowClassname({row}){
+            if(!row.isSuccess){
+                return 'disable-row'
+            }
+            return ''
+        },
+        changeModel(){
+            this.typeModel = this.infoForm.Model
+            this.resetForm()
+        },
+        //重置表单
+        resetForm(){
+            this.infoForm = _.cloneDeep(baseForm)
+            this.chartBatchData = null
+            this.factorTableData = []
+            this.factorTableDataSortCopy=[]
+            this.showMultipleChart = false
+            this.infoForm.Model = this.typeModel
+            this.typeModel = 0
+        },
+        //选择指标后获取指标详情
+        async getEdbDetail({EdbInfoId,EdbInfoType}) {
+            const { Data } = EdbInfoType 
+            ? await preDictEdbInterface.edbDetail({EdbInfoId})
+            : await dataBaseInterface.calculateDetail({EdbInfoId})
+            return { 
+                max: EdbInfoType ? Data.MaxValue : Data.EdbInfoDetail.MaxValue,
+                min: EdbInfoType ? Data.MinValue : Data.EdbInfoDetail.MinValue,
+                data:EdbInfoType ? Data : Data.EdbInfoDetail,
+            }
+        },
+        //选择指标
+        async handleSelectTarget({type,target}){
+            if(!target) return
+            this.infoForm[type] = target.EdbInfoId||''
+            const {max,min,data} = await this.getEdbDetail(target)
+            if(type==='IndTarget'){
+                this.IndTarget = data
+                //点计算时,才真正赋值ChartInfo
+                //this.multipleChartData.ChartInfo = data
+            }
+            //若为单因子,选择AB指标后预览曲线图
+            if(!['EdbInfoIdA','EdbInfoIdB'].includes(type)) return 
+            if(type==='EdbInfoIdA'){
+                this.infoForm.Curve.LeftMin = min;
+                this.infoForm.Curve.LeftMax = max;
+            }else if(type==='EdbInfoIdB'){
+                this.infoForm.Curve.RightMin = min;
+                this.infoForm.Curve.RightMax = max;
+            }
+            this.getPreviewSplineChart()
+        },
+        //单因子-选择指标后生成曲线图
+        async getPreviewSplineChart(){
+            if(!this.infoForm.EdbInfoIdA || !this.infoForm.EdbInfoIdB) return
+            if(this.infoForm.Curve.DateType===5&&!this.infoForm.Curve.Date[0]) return 
+            let params = {
+                ...this.infoForm 
+            }
+            const res = await chartRelevanceApi.previewSplineChart(params);
+            if(res.Ret !== 200) return
+            this.chartBatchData = this.chartBatchData?{
+                ...this.chartBatchData,
+                CurveData: res.Data.CurveData
+            }:{CurveData: res.Data.CurveData};
+        },
+        //单因子-曲线配置变化时重绘
+        changeSplineOption() {
+            const { LeftMin,LeftMax,RightMin,RightMax,IsOrder } = this.infoForm.Curve;
+            this.$refs.chartCard1.options.yAxis[0].max = Number(LeftMax);
+            this.$refs.chartCard1.options.yAxis[0].min = Number(LeftMin);
+
+            this.$refs.chartCard1.options.yAxis[1].max = Number(RightMax);
+            this.$refs.chartCard1.options.yAxis[1].min = Number(RightMin);
+            this.chartBatchData.CurveData.EdbInfoList[1].IsOrder = IsOrder;
+        },
+        //预览相关性图表/相关性矩阵
+        async previewChart(){
+            //表单校验
+            await this.$refs.infoFormRef.validate()
+            //相关性值校验
+            if(!this.checkValue()) return 
+            
+            if(this.infoForm.Model===1){
+                //单因子:渲染相关性图表,需要保存MultipleGraphConfigId
+                const res = await chartRelevanceApi.chartOptionsSet({...this.infoForm});
+                if(res.Ret !== 200) return
+                const { MultipleGraphConfigId } = res.Data;
+                this.infoForm.MultipleGraphConfigId = MultipleGraphConfigId;
+                this.previewSingleChart({})
+            }else{
+                //已添加指标后重新计算
+                if(this.isMultipleChartAdd){
+                    const {ChartInfoId,UniqueCode,ClassifyId} = this.multipleChartData.ChartInfo
+                    this.multipleChartData.ChartInfo = this.IndTarget
+                    this.multipleChartData.ChartInfo.ChartInfoId = ChartInfoId
+                    this.multipleChartData.ChartInfo.UniqueCode = UniqueCode
+                    this.multipleChartData.ChartInfo.ClassifyId = ClassifyId
+                }else{
+                    this.multipleChartData.ChartInfo = this.IndTarget
+                }
+                //多因子:根据分析周期生成相关性矩阵,在未添加曲线前不生成图表
+                this.previewMultipleTable()
+            }
+        },
+        //校验规则
+        checkValue() {
+            //只用于校验规则条件大小
+            let checkBool = true;
+            const valueMap = {
+                '年': 365,
+                '季': 90,
+                '月': 30,
+                '周': 7,
+                '天': 1
+            }
+            const { Correlation } = this.infoForm;
+            if(Correlation.CalculateValue*valueMap[Correlation.CalculateUnit] < Correlation.LeadValue*valueMap[Correlation.LeadUnit]*2) {
+                this.infoForm.Correlation.CalculateValue = 0;
+                this.$message.warning(/* '相关性计算窗口必须≥2*分析周期' */this.$t('StatisticAnalysis.ChartRelevance.check_value_hint'))
+                checkBool = false
+            }
+            return checkBool
+        },
+        //预览单因子图表 初始化 chartBatchData
+        async previewSingleChart(initConfig=null){
+            const res = await chartRelevanceApi.previewChartBatch({...this.infoForm});
+            if(res.Ret !== 200) return
+            this.chartBatchData = res.Data;
+            //第一次初始化时,需初始化图例和数据来源信息
+            if(initConfig){
+                const {CorrelationExtraConfig,SourcesFrom} = initConfig
+                try{
+                    this.chartBatchData.SourcesFrom = SourcesFrom?JSON.parse(SourcesFrom):_.cloneDeep(baseSourcesFrom)
+                }catch(e){
+                    this.chartBatchData.SourcesFrom = _.cloneDeep(baseSourcesFrom)
+                }
+                //新增时 来源与基本配置设置的一致
+                if(!this.$route.query.code){
+                    this.chartBatchData.SourcesFrom.isShow = this.SourcesFromVisable
+                }
+                //拼接来源
+                const {EdbInfoList} = res.Data.CorrelationData
+                const tempStr = EdbInfoList[0].SourceName+','+EdbInfoList[1].SourceName
+                this.chartBatchData.SourcesFrom.text += `${this.chartBatchData.SourcesFrom.text.length?',':''}${tempStr}`
+                //拼接后去重
+                let concatSourceArr = `${this.chartBatchData.SourcesFrom.text}`.split(',');
+                let sourceStr = Array.from(new Set(concatSourceArr)).join(',');
+                this.chartBatchData.SourcesFrom.text = sourceStr
+                try{
+                    const {LegendConfig} = CorrelationExtraConfig?JSON.parse(CorrelationExtraConfig):{LegendConfig:[]}
+                    this.chartBatchData.CorrelationData.YDataList[0].Color = LegendConfig[0].Color
+                    this.chartBatchData.CorrelationData.YDataList[0].Name = LegendConfig[0].Legendlame
+                }catch(e){
+
+                }
+            }
+        },
+        //预览多因子矩阵 初始化 factorTableData
+        previewMultipleTable(){
+            //全局loading
+            this.previewMatrixLoading = true
+            const {IndTarget,Correlation} = this.infoForm
+            const SeriesIds = this.$refs.multipleIndForm.factorList.map(i=>i.SeriesId)
+            //清空表格和图表
+            this.factorTableData = []
+            this.factorTableDataSortCopy=[]
+            this.multipleChartData.YDataList=[]
+            this.showMultipleChart = false
+            chartRelevanceApi.getCorrelationMatrix({
+                BaseEdbInfoId:IndTarget,
+                Correlation,
+                SeriesIds,
+            }).then(res=>{
+                //loading结束
+                this.previewMatrixLoading = false
+                if(res.Ret!==200) return
+
+                const {Fail=[],Success=[]} = res.Data
+                if(!Success||Success&&!Success.length){
+                    this.$message.warning('所有指标计算失败,请重新选择指标')
+                    return 
+                }
+                //标记失败的
+                const FailList = Fail?Fail.map(i=>{
+                    return {
+                        ...i,
+                        isSuccess:false,
+                        dataList:[]
+                    }
+                }):[]
+                const SuccessList = Success.map(i=>{
+                    return {
+                        ...i,
+                        isSuccess:true,
+                        isAdd:false,
+                        dataList:i.Values.map(v=>v.YData)
+                    }
+                })
+                this.factorTableData = [...SuccessList,...FailList]
+                this.factorTableDataSortCopy = _.cloneDeep(this.factorTableData)
+                //获取分析周期,生成±分析周期的数组
+                const {LeadValue,LeadUnit,CalculateUnit,CalculateValue} = this.infoForm.Correlation
+                this.SortedArray = generateSortedArray(LeadValue)
+                //设置图表默认值
+                this.multipleChartData.ChartInfo.Source = 3
+                this.multipleChartData.ChartInfo.ChartName = this.multipleChartData.ChartInfo.EdbName+`相关性分析(${CalculateValue}${CalculateUnit})`
+                this.multipleChartData.CorrelationChartInfo = {
+                    LeadValue:LeadValue,
+                    LeadUnit:LeadUnit
+                }
+                this.multipleChartData.XEdbIdValue = generateXEdbValue(LeadValue)
+            })
+        },
+        checkRecalculate(){
+            //若当前已预览过图表/矩阵,则重新请求矩阵接口,初始化矩阵和图表数据
+            if(this.showMultipleChart||this.factorTableData.length){
+                this.previewMultipleTable()
+            }
+        },
+        //相关性矩阵-添加曲线
+        addCurve(row){
+            const length = this.multipleChartData.YDataList.length
+            if(length>=10) return this.$message.warning('最多只支持添加10条曲线')
+            this.showMultipleChart = true
+            
+            const colors = ['#00f','#f00','#999','#000','#7cb5ec', '#90ed7d', '#f7a35c', '#8085e9', '#f15c80', '#e4d354', '#2b908f', '#f45b5b', '#91e8e1']
+            this.multipleChartData.YDataList.push({
+                Id:row.EdbInfoId,
+                SeriesId:row.SeriesId,
+                Value:generateYDataValue(row.dataList,this.infoForm.Correlation.LeadValue),
+                Color:colors[length%colors.length],
+                Name:row.EdbName,
+                NameEn:row.EdbNameEn
+            })
+            row.isAdd = true
+            //数据来源拼接
+            this.multipleChartData.SourcesFrom.text += `${this.multipleChartData.SourcesFrom.text.length?',':''}${row.SourceName}`
+            //拼接后去重
+            let concatSourceArr = `${this.multipleChartData.SourcesFrom.text}`.split(',');
+            let sourceStr = Array.from(new Set(concatSourceArr)).join(',');
+            this.multipleChartData.SourcesFrom.text = sourceStr
+        },
+        //相关性矩阵-删除曲线
+        deleteCurve(row){
+            const index = this.multipleChartData.YDataList.findIndex(i=>i.Id===row.EdbInfoId&&i.SeriesId===row.SeriesId)
+            index!==-1&&this.multipleChartData.YDataList.splice(index,1)
+            row.isAdd = false
+            if(!this.multipleChartData.YDataList.length){
+                this.showMultipleChart = false
+            }
+        },
+        //打开图例设置弹窗
+        handleChangeChartSetting(data){
+            //单多因子的data结构是相同的
+            /**
+             * 图表名称:data.ChartName/en
+             * 图例:data.YDataList.Color data.YDataList.Name
+             * 数据来源:新字段 SourcesFrom.text
+             * 数据来源开关:新字段 SourcesFrom.isShow
+             */
+            const SourcesFrom = this.infoForm.Model===1?_.cloneDeep(this.chartBatchData.SourcesFrom):_.cloneDeep(this.multipleChartData.SourcesFrom)
+            this.settingData = {
+                SourcesFrom,
+                chartName:data.ChartName,
+                YDataList:_.cloneDeep(data.YDataList)
+            }
+            this.isSettingChartShow = true
+        },
+        //图例设置回调
+        changeChartSettingBack(){
+            const {chartName,YDataList,SourcesFrom} = this.settingData
+            if(this.infoForm.Model===1){
+                this.chartBatchData.CorrelationData.ChartInfo.ChartName = chartName
+                this.chartBatchData.CorrelationData.YDataList = YDataList
+                this.chartBatchData.SourcesFrom = SourcesFrom
+            }else{
+                //多因子
+                this.multipleChartData.ChartInfo.ChartName = chartName
+                this.multipleChartData.YDataList = YDataList
+                this.multipleChartData.SourcesFrom = SourcesFrom
+            }
+            this.isSettingChartShow = false
+        },
+        //打开保存/另存为图表/指标弹窗
+        handleSave({type,chartData,scence}){
+            this.saveSource = type
+            this.chartData = chartData;
+            this.saveScence = scence;
+           /*  this.isSaveEdbToBase = true;
+            this.isSaveChartToBase = true; */
+        },
+        //单因子-保存/另存为指标回调
+        saveEdbBack({source}){
+            if(source===3) this.buttonAuth.isRollEdbAdd = true;
+            else if(source===4)  this.buttonAuth.isRollEdbTwoAdd = true;
+        },
+        //单因子-保存/另存为图表回调
+        saveChartBack({source,id}){
+            this.$message.success(this.$t('MsgPrompt.saved_msg'));
+            this.setButtonAuth(source);
+            //设置封面图
+            this.$refs[`chartCard`+source].setChartImage(source,id)
+        },
+        //单因子-保存图表后按钮控制 
+        setButtonAuth(source) {
+            const sourceMap = {
+                1: 'isCurveChartAdd',
+                2: 'isRelevanceChartAdd',
+                3: 'isRollChartAdd',
+                4: 'isRollChartTwoAdd'
+            }
+            this.buttonAuth[sourceMap[source]] = true;
+        },
+        //编辑-获取相关性图表详情
+        getRelevanceChartDetail(){
+            //判断是单因子or多因子
+            if(this.$route.query.type!=1){
+                this.getSingleDetail()
+            }else{
+                this.getMultiplyDetail()
+            }
+            
+        },
+        //获取单因子图表详情
+        async getSingleDetail(){
+            if(!this.$route.query.code) return
+            const res = await chartRelevanceApi.getOptionByCode({ UniqueCode: this.$route.query.code});
+            if(res.Ret !== 200) return 
+            this.chartInfoData = {
+                EdbInfoList: res.Data.EdbInfoList
+            }
+            //初始化infoForm
+            const { MultipleGraphConfigId,EdbInfoIdA,EdbInfoIdB,Curve,Correlation,RollingCorrelation } = res.Data.MultipleGraphConfig;
+            this.infoForm = {
+                Model:1,
+                MultipleGraphConfigId,
+                EdbInfoIdA,
+                EdbInfoIdB,
+                Curve: {
+                ...JSON.parse(Curve),
+                Date: [JSON.parse(Curve).StartDate,JSON.parse(Curve).EndDate],
+                },
+                Correlation: JSON.parse(Correlation),
+                RollingCorrelation: JSON.parse(RollingCorrelation)
+            }
+            //初始化权限
+            this.buttonAuth =  {
+                isCurveChartAdd: res.Data.ChartMappingList.some(_ => _.MultipleLocationSource===1),
+                isRelevanceChartAdd: res.Data.ChartMappingList.some(_ => _.MultipleLocationSource===2),
+                isRollChartAdd: res.Data.ChartMappingList.some(_ => _.MultipleLocationSource===3),
+                isRollEdbAdd: res.Data.EdbMappingList.some(_ => _.MultipleLocationSource===3),
+                isRollChartTwoAdd: res.Data.ChartMappingList.some(_ => _.MultipleLocationSource===4),
+                isRollEdbTwoAdd: res.Data.EdbMappingList.some(_ => _.MultipleLocationSource===4)
+            }
+            //初始化chartBatch
+            const RelevanceInfo = res.Data.ChartMappingList.find(_=>_.MultipleLocationSource===2)||{}
+            const {CorrelationExtraConfig,SourcesFrom} = RelevanceInfo
+            this.previewSingleChart({CorrelationExtraConfig,SourcesFrom})
+        },
+        //获取多因子图表详情
+        getMultiplyDetail(){
+            if(!this.$route.query.code) return
+            //获取图表详情
+            chartRelevanceApi.getMultipleChartDetail({
+                UniqueCode:this.$route.query.code
+            }).then(res=>{
+                if(res.Ret!==200) return
+                const {ChartName,ChartClassifyId,ChartInfoId,UniqueCode,ExtraConfig,SourcesFrom} = res.Data.ChartInfo
+                this.multipleChartData.ChartInfo = {
+                    ClassifyId:ChartClassifyId,ChartName,ChartInfoId,UniqueCode,Source:3
+                }
+                const {EdbInfoList,XEdbIdValue,YDataList,CorrelationChartInfo} = res.Data
+                const {LeadValue,LeadUnit} = CorrelationChartInfo
+                this.multipleChartData.CorrelationChartInfo = {
+                    LeadValue,LeadUnit
+                }
+                this.multipleChartData.XEdbIdValue = XEdbIdValue
+                this.multipleChartData.EdbInfoList = EdbInfoList
+                this.multipleChartData.YDataList = YDataList.map(i=>{
+                    return {
+                        ...i,
+                        Id:i.SeriesEdb.EdbInfoId,
+                        SeriesId:i.SeriesEdb.SeriesId
+                    }
+                })
+                try{
+                    this.multipleChartData.SourcesFrom = SourcesFrom?JSON.parse(SourcesFrom):_.cloneDeep(baseSourcesFrom)
+                }catch(e){
+                    this.multipleChartData.SourcesFrom = _.cloneDeep(baseSourcesFrom)
+                }
+                this.showMultipleChart = true
+                this.isMultipleChartAdd = true
+
+            })
+            //获取表单及矩阵详情
+            chartRelevanceApi.getMultipleFactorDetail({
+                UniqueCode:this.$route.query.code
+            }).then(res=>{
+                if(res.Ret!==200) return 
+                const {BaseEdbInfo,CorrelationConfig,EdbSeries,CorrelationMatrix} = res.Data
+                const {LeadValue,LeadUnit,CalculateValue,CalculateUnit} = CorrelationConfig
+                this.infoForm.Model = 2
+                //基础信息表单
+                this.infoForm = {
+                    Model:2,
+                    IndTarget:BaseEdbInfo.EdbInfoId,
+                    Correlation:{
+                        LeadValue,LeadUnit,CalculateValue,CalculateUnit
+                    }
+                }
+                //标的因子信息
+                this.IndTarget = _.cloneDeep(BaseEdbInfo)
+                this.chartInfoData = {
+                    EdbInfoList:[BaseEdbInfo]
+                }
+                //因子列表
+                this.SeriesList = EdbSeries
+                //相关性矩阵
+                this.factorTableData = CorrelationMatrix.map(i=>{
+                    return {
+                        ...i,
+                        isSuccess:true,
+                        isAdd:i.Used,
+                        dataList:i.Values.map(v=>v.YData)
+                    }
+                })
+                this.factorTableDataSortCopy = _.cloneDeep(this.factorTableData)
+                this.SortedArray = generateSortedArray(LeadValue)
+            })
+        },
+        //多因子-保存/更新/另存为图表
+        async saveMultipleChart({ChartName,ClassifyId,type}){
+            const {IndTarget,Correlation} = this.infoForm
+            const {SourcesFrom,YDataList} = this.multipleChartData
+            const SeriesEdb = YDataList.map(i=>{
+                return {
+                    SeriesId:i.SeriesId,
+                    EdbInfoId:i.Id
+                }
+            })
+            //BaseEdbInfoId优先取当前图表内的EdbInfoId,若没有值取表单内的
+            const BaseEdbInfoId = this.multipleChartData.ChartInfo.EdbInfoId||IndTarget
+            const SeriesIds = this.$refs.multipleIndForm.factorList.map(i=>i.SeriesId)
+            const LegendConfig = YDataList.map(i=>{
+                return {
+                    LegendName:i.Name,
+                    SeriesId:i.SeriesId,
+                    EdbInfoId:i.Id,
+                    Color:i.Color
+                }
+            })
+            let params = {
+                ChartName,ClassifyId,
+                AnalysisMode:1,
+                BaseEdbInfoId:BaseEdbInfoId/* IndTarget */,
+                FactorCorrelation:{
+                    ...Correlation,
+                    SeriesEdb,
+                    SeriesIds
+                },
+                ExtraConfig:{
+                    LegendConfig,
+                },
+                SourcesFrom,
+            }
+            const {ChartInfoId} = this.multipleChartData.ChartInfo
+            const isEdit = ChartInfoId&&type!=='saveOther'
+            //另存为/新增->add 更新->edit
+            const res = isEdit
+                ?await chartRelevanceApi.editMultipleFactor({...params,ChartInfoId})
+                :await chartRelevanceApi.addMultipleFactor({...params,SaveAs:type==='saveOther'})
+            //计算中...
+            if(res.Ret!==200) return 
+            if(!isEdit){
+                this.isMultipleChartAdd = true
+                this.multipleChartData.ChartInfo.ChartInfoId = res.Data.ChartInfoId
+                this.multipleChartData.ChartInfo.UniqueCode = res.Data.UniqueCode
+                this.multipleChartData.ChartInfo.ClassifyId = res.Data.ClassifyId
+            }
+            //更新图表名称
+            this.multipleChartData.ChartInfo.ChartName = ChartName
+            this.$message.success(`${isEdit?'更新':'保存'}成功`)
+            //设置缩略图
+            this.$refs[`chartCard`+2].setChartImage(2,res.Data.ChartInfoId)
+            this.isSaveChartToBase = false
+        },
+    },
+};
+</script>
+
+<style lang="scss">
+.relevance-chart-editor-wrap{
+    display: flex;
+    *{
+        box-sizing: border-box;
+    }
+    .content-wrap{
+        background: #fff;
+        border: 1px solid #ececec;
+        border-radius: 4px;
+        .source{
+            padding-left: 20px;
+            padding-bottom:20px;
+            margin-top: -20px;
+        }
+    }
+    .info-wrap{
+        width:380px;
+        min-width: 380px;
+        margin-right: 20px;
+        display: flex;
+        flex-direction: column;
+        height: calc(100vh - 120px);
+        .info-top{
+            padding: 15px 20px;
+            border-bottom: 1px solid #ececec;
+            box-shadow: 0px 3px 6px rgba(167, 167, 167, 0.09);
+            display: flex;
+            align-items: center;
+            >span{
+                color:#0052D9;
+                cursor: pointer;
+                flex: 1;
+                text-align: right;
+                img{
+                    vertical-align: middle;
+                }
+            }
+        }
+        .info-form-wrap{
+            flex: 1;
+            overflow-y: auto;
+            padding:20px;
+            .el-form-item{
+                margin-bottom: 10px;
+            }
+            .form-box{
+                margin-top: 10px;
+                padding-top: 10px;
+                border-top:1px dashed #DCDFE6;
+            }
+            .model-form{
+                .select-target{
+                    display: flex;
+                    margin-top: 10px;
+                    .el-form-item__label{
+                        flex-shrink: 0;
+                    }
+                    .el-form-item__content{
+                        margin-left: 0 !important;
+                        .el-select{
+                            margin-top: 10px !important;
+                        }
+                    }
+                    .el-date-editor.el-input, .el-date-editor.el-input__inner{
+                        width: auto;
+                    }
+                }
+                .flex-form-item{
+                    .el-form-item__content{
+                        display: flex;
+                        gap:0 5px;
+                    }
+                }
+            }
+            .multiple-model-form{
+                .factor-form-item{
+                    .el-form-item {
+                        margin-bottom: 0;
+                    }
+                    .el-form-item__label{
+                        width:auto !important;
+                    }
+                    .factor-list{
+                        .list-item{
+                            padding:10px 20px;
+                            background-color: #EBEFF6;
+                            border:1px solid #C8CDD9;
+                            cursor: pointer;
+                            display: flex;
+                            justify-content: space-between;
+                            align-items: center;
+                            border-bottom: none;
+                            &:last-child{
+                                border-bottom: 1px solid #C8CDD9;
+                            }
+                        }
+                    }
+                    .add-factor-btn{
+                        margin-top: 20px;
+                        display: flex;
+                        gap:10px;
+                        align-items: center;
+                        cursor: pointer;
+                        color:#0052D9;
+                        img{
+                            width: 15px;
+                            height: 15px;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    .model-wrap{
+        flex:1;
+        height: calc(100vh - 120px);
+        overflow-x: hidden;
+        .single-model-wrap{
+            width: 100%;
+            overflow: hidden;
+        }
+        .chart-min-cont {
+            height: calc(100vh - 120px);
+            overflow-y: auto;
+            display: flex;
+            flex-wrap: wrap;
+            gap:15px;
+            .card-wrapper {
+                width: 48%;
+                min-height: 350px;
+                min-width:410px;
+                .card-item {
+                    padding: 20px;
+                    .top {
+                        display: flex;
+                        justify-content: space-between;
+                        align-items: center;
+                        margin-bottom: 15px;
+                    }
+                    .title {
+                        font-size: 15px;
+                        text-align: left;
+                        margin: 10px 0;
+                    }
+                }
+            }
+        }
+        .nodata {
+            height: calc(100vh - 120px);
+            background-color: #fff;
+            text-align: center;
+            font-size: 16px;
+            color: #666;
+            padding: 100px 0;
+        }
+        .multiple-model-wrap{
+            display: flex;
+            flex-direction: column;
+            justify-content: space-between;
+            height: calc(100vh - 120px);
+            .relevant-matrix-table-box,.relevant-chart-box{
+                height: 49%;
+                overflow: auto;
+            }
+            
+            .relevant-matrix-table-box{
+                display: flex;
+                .el-table{
+                    flex:1;
+                    thead{
+                        color:#333;
+                    }
+                    th.zip-cell{
+                        padding:0;
+                        font-size: 12px;
+                        background: #F5F7FA !important;
+                        .cell{
+                            padding:0;
+                            .caret-wrapper{
+                                height:24px;
+                                width:20px;
+                                .sort-caret.ascending{
+                                    top:0;
+                                }
+                                .sort-caret.descending{
+                                    bottom:0;
+                                }
+                            }
+                        }
+                    }
+                    td.zip-cell{
+                        padding:0;
+                        font-size: 12px;
+                        background-color: #fff !important;
+                        .cell{
+                            padding:0;
+                            line-height: 23px;//调整单元格高度
+                        }
+                    }
+                }
+            }
+            .relevant-chart-box{
+                .card-item {
+                    padding: 20px;
+                    .top {
+                        display: flex;
+                        justify-content: space-between;
+                        align-items: center;
+                        .title {
+                            font-size: 15px;
+                            text-align: left;
+                            margin: 10px 0;
+                        }
+                    }
+                    
+                }
+            }
+        }
+    }
+    .disable-row{
+        pointer-events: none;
+        opacity: 0.5;
+        cursor: default; 
+    }
+}
+</style>

+ 111 - 0
src/views/chartRelevance_manage/relevance/utils/config.js

@@ -0,0 +1,111 @@
+//相关性表单
+export const baseForm = {
+    Model:1,//模式 1单因子 2 多因子
+    EdbInfoIdA:'',//指标A
+    EdbInfoIdB:'',//指标B
+    IndTarget:'',//标的指标
+    Curve:{//曲线图
+        DateType:3,
+        Date:'',
+        StartDate:'',
+        EndDate:'',
+        LeftMin:'',
+        LeftMax:'',
+        RightMin:'',
+        RightMax:'',
+        EdbInfoType:true,//显示为标准指标(true)或领先指标(false)
+        LeadValue:0,
+        LeadUnit:'天',
+    },
+    Correlation:{//相关性
+        CalculateValue:0,//计算窗口
+        CalculateUnit:'天',//计算窗口单位(时间)
+        LeadValue:0,//分析周期
+        LeadUnit:'天',//分析周期单位(时间)
+    },
+    RollingCorrelation:[//滚动相关性
+        {
+            CalculateValue:0,
+            CalculateUnit:'天',
+            LeadValue:0,
+            LeadUnit:'天'
+        },
+        {
+            CalculateValue:0,
+            CalculateUnit:'天',
+            LeadValue:0,
+            LeadUnit:'天'
+        }
+    ],
+}
+//多因子图表信息
+export const baseChartInfo = {
+    ChartInfo:{
+        Source:3,
+        ChartName:'',//与单因子保持一致,用于保存/另存为回显
+        ChartInfoId:'',//新增前为空
+        UniqueCode:'',//新增前为空
+        ClassifyId:'',//新增前为空
+        /**
+         * EdbInfoId 当前页面点击计算后有值 标的指标的ID
+         * EdbName 当前页面点击计算后有值 标的指标的名称
+         */
+    },
+    EdbInfoList:[],//第一项为标的指标,其他项为添加的指标
+    XEdbIdValue:[],//SourtedArray
+    CorrelationChartInfo:{
+        LeadValue:0,//分析周期
+        LeadUnit:'天',//分析周期单位(时间)
+    },
+    YDataList:[
+        /* {
+            Value:[],//factorTableData.dataList
+            Color:'#00f',
+            Name:'',//factorTableData.name
+            NameEn:''
+        } */
+    ],
+    SourcesFrom:{
+        isShow:true,//是否显示
+        text:'',//内容
+        color:'#333',
+        fontSize:12,
+    },//
+}
+//多因子-相关性矩阵表格
+const factorTableData = [
+    {
+        EdbInfoId:'指标ID',
+        SeriesId:'指标所属系列ID',//与EdbInfoId一起表示指标的唯一性
+        EdbName:'指标名称',
+        dataList:[],//领先期数数据,下标需和期数对应
+        isAdd:false,//是否已添加曲线
+    }
+]
+//多因子-因子系列
+const factorList = [
+    {
+        "SeriesId": 4, //系列Id
+        "SeriesName": "系列名称A001", //系列名称
+        "EdbInfoType": 0, //0指标,1预测指标
+        "CalculateStep":[ //计算公式
+            {
+                "Formula": "", //N值/移动天数/指数修匀alpha值/计算公式等
+                "Calendar": "", //公历/农历
+                "Source": 3, //计算方式来源
+                "Sort": 1 
+            },
+        ],
+        "EdbMappings":[
+            
+        ],//所选的指标
+    },
+]
+
+//数据来源
+export const baseSourcesFrom = {
+    isShow:true,//是否显示
+    text:'',//内容
+    color:'#333',
+    fontSize:12,
+}

+ 70 - 0
src/views/chartRelevance_manage/relevance/utils/index.js

@@ -0,0 +1,70 @@
+//相关性矩阵表头
+export const generateSortedArray = (N)=>{
+    let positiveArr = [];
+    for (let i = 0; i <= N; i++) {
+        positiveArr.push(i);
+    }
+    
+    let negativeArr = [];
+    for (let i = -1; i >= -N; i--) {
+        negativeArr.push(i);
+    }
+    
+    return positiveArr.concat(negativeArr);
+}
+//mock用 相关性图表x轴
+export const generateXEdbValue = (N)=>{
+    let positiveArr = [];
+    for (let i = 0; i <= N; i++) {
+        positiveArr.push(i);
+    }
+    
+    let negativeArr = [];
+    for (let i = -N; i <= -1; i++) {
+        negativeArr.push(i);
+    }
+    return [...negativeArr,...positiveArr]
+}
+//转换YDataList data是领先期数从0开始的数组
+export const generateYDataValue = (data,n)=>{
+    //取[n+1,length]项 为-1~-n 翻转为-n~-1
+    let arr = data.slice(n+1).reverse()
+    //data从下标n+1处放置
+    let dataArr = data.slice(0,n+1)
+    return [...arr,...dataArr]
+}
+//判断两个指标ID数组是否有变化:指标ID一致视为无变化
+export const checkListChange = (listA,listB)=>{
+    // 将两个数组转换为 Set 对象
+    const setA = new Set(listA);
+    const setB = new Set(listB);
+
+    if (setA.size !== setB.size) {
+        return true;
+    }
+    // 检查 setA 中的每一项是否都在 setB 中
+    for (const item of setA) {
+        if (!setB.has(item)) {
+            return true;
+        }
+    }
+    // 如果所有检查都通过,说明两个数组包含相同的项
+    return false;
+}
+//判断两个计算方式是否有变化:顺序,Source以及Formula的值一致视为无变化
+export const checkStepsChange = (stepsA,stepsB)=>{
+    if(stepsA.length!==stepsB.length) return true
+
+     // 逐个比较数组中的每一项是否都相同且顺序一致
+     for (let i = 0; i < stepsA.length; i++) {
+        if (  stepsA[i].Sort!==stepsB[i].Sort
+            ||stepsA[i].Source!==stepsB[i].Source
+            ||stepsA[i].Formula!==stepsB[i].Formula) {
+            return true
+        }
+        if(stepsA[i].Source===11&&stepsA[i].Calendar!==stepsB[i].Calendar){
+            return true
+        }
+    }
+    return false
+}

+ 17 - 4
src/views/dataEntry_manage/components/SaveChartOther.vue

@@ -33,7 +33,8 @@
             label: currentLang==='en'?'ChartClassifyNameEn':'ChartClassifyName',
             label: currentLang==='en'?'ChartClassifyNameEn':'ChartClassifyName',
             value: 'ChartClassifyId',
             value: 'ChartClassifyId',
             children: 'Children',
             children: 'Children',
-            emitPath: false
+            emitPath: false,
+            checkStrictly:isRelevanceChart
           }"
           }"
           style="width: 80%"
           style="width: 80%"
           :placeholder="$t('Chart.InputHolderAll.input_classify')"
           :placeholder="$t('Chart.InputHolderAll.input_classify')"
@@ -75,6 +76,11 @@ export default {
       }
       }
     }
     }
   },
   },
+  computed:{
+    isRelevanceChart(){
+        return ['/chartrelevance'].includes(this.$route.path)||this.source===3||this.source===4
+    }
+  },
   data() {
   data() {
     return {
     return {
       form: {
       form: {
@@ -109,7 +115,7 @@ export default {
       if([2,5,'good_price'].includes(this.source)){//商品价格
       if([2,5,'good_price'].includes(this.source)){//商品价格
         res=await futuresInterface.classifyList()
         res=await futuresInterface.classifyList()
       }else if([3,4,'relevance_chart'].includes(this.source)){//相关性图表
       }else if([3,4,'relevance_chart'].includes(this.source)){//相关性图表
-        res=await chartRelevanceApi.classifyList()
+        res=await chartRelevanceApi.classifyTree()
       }else if([6,'fitting_equation'].includes(this.source)){//拟合方程
       }else if([6,'fitting_equation'].includes(this.source)){//拟合方程
         res=await fittingEquationInterface.classifyList()
         res=await fittingEquationInterface.classifyList()
       }else if([7,8,9,'statistic_feature'].includes(this.source)) { //标准差 百分位 频率图
       }else if([7,8,9,'statistic_feature'].includes(this.source)) { //标准差 百分位 频率图
@@ -125,7 +131,7 @@ export default {
       if([2,5,'good_price'].includes(this.source)){//商品价格
       if([2,5,'good_price'].includes(this.source)){//商品价格
         this.filterNodes(res.Data.AllNodes,1)
         this.filterNodes(res.Data.AllNodes,1)
       }else if([3,4,'relevance_chart'].includes(this.source)){//相关性图表
       }else if([3,4,'relevance_chart'].includes(this.source)){//相关性图表
-        this.filterNodes(res.Data.AllNodes,1)
+        this.filterNodesAll(res.Data.AllNodes)
       }else if([6,'fitting_equation'].includes(this.source)){//拟合方程
       }else if([6,'fitting_equation'].includes(this.source)){//拟合方程
         this.filterNodes(res.Data.AllNodes,1)
         this.filterNodes(res.Data.AllNodes,1)
       }else if([7,8,9,'statistic_feature'].includes(this.source)) { //标准差 百分位 频率图
       }else if([7,8,9,'statistic_feature'].includes(this.source)) { //标准差 百分位 频率图
@@ -139,7 +145,14 @@ export default {
 			this.classifyOptions = res.Data.AllNodes || [];
 			this.classifyOptions = res.Data.AllNodes || [];
     
     
 		},
 		},
-
+		filterNodesAll(arr){
+			arr.length && arr.forEach(item => {
+				item.Children && item.Children.length && this.filterNodesAll(item.Children)
+				if(!item.Children.length) {
+					delete item.Children
+				}
+			})
+		},
 		// 递归改变第三级目录结构
 		// 递归改变第三级目录结构
 		filterNodes(arr,n) {
 		filterNodes(arr,n) {
 			arr.length && arr.forEach(item => {
 			arr.length && arr.forEach(item => {

+ 2 - 2
src/views/dataEntry_manage/mixins/chartPublic.js

@@ -677,11 +677,11 @@ export const chartSetMixin = {
     /* 切换相关性图中英文 */
     /* 切换相关性图中英文 */
     changeRelevanceLang(){
     changeRelevanceLang(){
         this.options.yAxis.forEach(item => {
         this.options.yAxis.forEach(item => {
-          item.title.text = this.currentLang == 'zh' ? item.title.textCh : item.title.textEn
+          item.title.text = this.currentLang == 'zh' ? item.title.textCh : item.title.textEn||item.title.textCh
         });
         });
         //图例
         //图例
         this.options.series.forEach(item => {
         this.options.series.forEach(item => {
-          item.name = this.currentLang == 'zh' ? item.nameCh : item.nameEn
+          item.name = this.currentLang == 'zh' ? item.nameCh : item.nameEn||item.nameCh
         });
         });
         //tooltip
         //tooltip
         this.options.tooltip.formatter = this.currentLang == 'zh' ? this.options.tooltip.formatterCh : this.options.tooltip.formatterEn
         this.options.tooltip.formatter = this.currentLang == 'zh' ? this.options.tooltip.formatterCh : this.options.tooltip.formatterEn

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

@@ -680,11 +680,15 @@ export default {
                   {
                   {
                     Value: res.Data.YDataList[0].Value,
                     Value: res.Data.YDataList[0].Value,
                     Color:'#00f',
                     Color:'#00f',
-                    Name:res.Data.ChartInfo.ChartName,
-                    NameEn:res.Data.ChartInfo.ChartNameEn
+                    Name:res.Data.YDataList[0].Name||res.Data.ChartInfo.ChartName,
+                    NameEn:res.Data.YDataList[0].NameEn||res.Data.ChartInfo.ChartNameEn
                   }
                   }
                 ]
                 ]
             }
             }
+            //多因子
+            if(res.Data.CorrelationChartInfo.AnalysisMode===1){
+                this.relevanceChartData.YDataList = res.Data.YDataList
+            }
             
             
             this.initRelevanceChartData()
             this.initRelevanceChartData()
             this.tableData = res.Data.EdbInfoList
             this.tableData = res.Data.EdbInfoList
@@ -1315,12 +1319,19 @@ export default {
     editChartHandle() {
     editChartHandle() {
       
       
       let path = '';
       let path = '';
+      let type = '';
       if(this.chartInfo.Source === 1){
       if(this.chartInfo.Source === 1){
         path='/editchart'
         path='/editchart'
       }else if([2,5].includes(this.chartInfo.Source)){
       }else if([2,5].includes(this.chartInfo.Source)){
         path='/addCommodityChart'
         path='/addCommodityChart'
       }else if([3,4].includes(this.chartInfo.Source)){
       }else if([3,4].includes(this.chartInfo.Source)){
-        path='/relevancechartEditor'
+        //path='/relevancechartEditor'
+        path='/relevancechartEditorV2'
+        if(this.chartInfo.Source===3){
+            type = this.relevanceChartData.CorrelationChartInfo&&this.relevanceChartData.CorrelationChartInfo.AnalysisMode||0
+        }else{//滚动相关性也是单因子
+            type = 0
+        }
       }else if(this.chartInfo.Source===6){
       }else if(this.chartInfo.Source===6){
         path='/fittingEquationChartEditor'
         path='/fittingEquationChartEditor'
       }else if([7,8,9].includes(this.chartInfo.Source)) {
       }else if([7,8,9].includes(this.chartInfo.Source)) {
@@ -1334,7 +1345,8 @@ export default {
         path,
         path,
         query: {
         query: {
           code: this.chartInfo.UniqueCode,
           code: this.chartInfo.UniqueCode,
-          from: 'mychart'
+          from: 'mychart',
+          type,
         }
         }
       })
       })
       window.open(href,'_blank');
       window.open(href,'_blank');

+ 15 - 11
src/views/ppt_manage/mixins/pptMixins.js

@@ -306,17 +306,21 @@ export default {
       }else if([3].includes(this.chartInfo.Source)){//相关性
       }else if([3].includes(this.chartInfo.Source)){//相关性
         this.relevanceChartData={
         this.relevanceChartData={
             ChartInfo:res.Data.ChartInfo,
             ChartInfo:res.Data.ChartInfo,
-						EdbInfoList:res.Data.EdbInfoList,
-						XEdbIdValue:this.chartInfo.Source === 3 ? res.Data.XEdbIdValue : res.Data.DataResp.XDateTimeValue,
-						CorrelationChartInfo:res.Data.CorrelationChartInfo,
-						YDataList:[
-							{
-								Value:this.chartInfo.Source === 3 ? res.Data.YDataList[0].Value : res.Data.DataResp.YDataList[0].Value,
-								Color:'#00f',
-								Name:res.Data.ChartInfo.ChartName,
-								NameEn:res.Data.ChartInfo.ChartNameEn
-							}
-						]
+            EdbInfoList:res.Data.EdbInfoList,
+            XEdbIdValue:this.chartInfo.Source === 3 ? res.Data.XEdbIdValue : res.Data.DataResp.XDateTimeValue,
+            CorrelationChartInfo:res.Data.CorrelationChartInfo,
+            YDataList:[
+                {
+                    Value:this.chartInfo.Source === 3 ? res.Data.YDataList[0].Value : res.Data.DataResp.YDataList[0].Value,
+                    Color:res.Data.YDataList[0].Color||'#00f',
+                    Name:res.Data.YDataList[0].Name||res.Data.ChartInfo.ChartName,
+                    NameEn:res.Data.YDataList[0].NameEn||res.Data.ChartInfo.ChartNameEn
+                }
+            ]
+        }
+        //多因子重新赋值YDataList
+        if(res.Data.CorrelationChartInfo.AnalysisMode===1){
+            this.relevanceChartData.YDataList = res.Data.YDataList
         }
         }
         this.initRelevanceChartData()
         this.initRelevanceChartData()
         this.changeRelevanceOptions(currentLang)
         this.changeRelevanceOptions(currentLang)

+ 2 - 1
src/views/ppt_manage/newVersion/utils/untils.js

@@ -721,6 +721,7 @@ export const getContextMenuPos = (layerId) =>{
     Source:1
     Source:1
     MyChartType: 2 季节性图,7 柱形图,  10 截面散点图,
     MyChartType: 2 季节性图,7 柱形图,  10 截面散点图,
     Source:2 商品价格曲线图
     Source:2 商品价格曲线图
+    Source:3,4 相关性/滚动相关性
     Source:5 利润曲线
     Source:5 利润曲线
     Source:6 拟合方程曲线
     Source:6 拟合方程曲线
     Source:7 统计特征-标准差
     Source:7 统计特征-标准差
@@ -730,7 +731,7 @@ export const getContextMenuPos = (layerId) =>{
 */
 */
 export const isShowPPTTitle = (Source,MyChartType)=>{
 export const isShowPPTTitle = (Source,MyChartType)=>{
     const sense_1 = Source===1&&[2,7,10].includes(MyChartType)
     const sense_1 = Source===1&&[2,7,10].includes(MyChartType)
-    const sense_2 = [2,5,6,7,8,9].includes(Source)
+    const sense_2 = [2,3,4,5,6,7,8,9].includes(Source)
     return sense_1||sense_2
     return sense_1||sense_2
 }
 }