Parcourir la source

Merge branch 'master' of http://8.136.199.33:3000/eta_front/eta_front into ETA1.3.8

cxmo il y a 11 mois
Parent
commit
e5f2889931
100 fichiers modifiés avec 6307 ajouts et 1028 suppressions
  1. 4 2
      src/api/api.js
  2. 2 2
      src/api/http.js
  3. 338 1
      src/api/modules/chartRelevanceApi.js
  4. 10 0
      src/api/modules/pptApi.js
  5. 10 0
      src/api/modules/pptEnApi.js
  6. 21 0
      src/api/modules/sheetApi.js
  7. 20 0
      src/api/modules/smartReport.js
  8. 60 2
      src/api/modules/thirdBaseApi.js
  9. BIN
      src/assets/img/icons/add_blue_new.png
  10. BIN
      src/assets/img/icons/ai_Company.png
  11. BIN
      src/assets/img/icons/ai_avatar.png
  12. BIN
      src/assets/img/icons/fullsreen.png
  13. BIN
      src/assets/img/icons/gpt-4-turbo.png
  14. BIN
      src/assets/img/icons/gpt-4.png
  15. BIN
      src/assets/img/icons/more.png
  16. BIN
      src/assets/img/smartReport/icon17.png
  17. BIN
      src/assets/img/smartReport/icon18.png
  18. 22 10
      src/components/chart/chartDetailHandlesWrap.vue
  19. 7 3
      src/components/chart/chartListWrap.vue
  20. 130 42
      src/components/lzTable.vue
  21. 21 7
      src/routes/modules/chartRoutes.js
  22. 16 0
      src/routes/modules/dataRoutes.js
  23. 21 2
      src/utils/buttonConfig.js
  24. 1 0
      src/utils/parseData.js
  25. 8 1
      src/utils/svgToblob.js
  26. 11 1
      src/views/Home.vue
  27. 12 12
      src/views/Login.vue
  28. 19 0
      src/views/chartFrame_manage/common/config.js
  29. 1 5
      src/views/chartFrame_manage/common/graph.js
  30. 31 6
      src/views/chartFrame_manage/components/frameContainer.vue
  31. 9 1
      src/views/chartFrame_manage/components/frameToolBar.vue
  32. 9 1
      src/views/chartFrame_manage/css/basePage.scss
  33. 1 1
      src/views/chartFrame_manage/css/customTree.scss
  34. 14 2
      src/views/chartFrame_manage/frameEditor.vue
  35. 16 8
      src/views/chartFrame_manage/index.vue
  36. 24 0
      src/views/chartRelevance_manage/components/explainText.js
  37. 670 0
      src/views/chartRelevance_manage/crossVarietyAnalysis/chartEditor.vue
  38. 208 0
      src/views/chartRelevance_manage/crossVarietyAnalysis/components/chartFormSection.vue
  39. 140 0
      src/views/chartRelevance_manage/crossVarietyAnalysis/components/edbTableSection.vue
  40. 182 0
      src/views/chartRelevance_manage/crossVarietyAnalysis/components/tagRelationDialog.vue
  41. 207 0
      src/views/chartRelevance_manage/crossVarietyAnalysis/components/tagSetDialog.vue
  42. 570 0
      src/views/chartRelevance_manage/crossVarietyAnalysis/list.vue
  43. 1 1
      src/views/chartRelevance_manage/css/index.scss
  44. 4 4
      src/views/chartRelevance_manage/fittingEquation/fittingEquationChartEditor.vue
  45. 3 3
      src/views/chartRelevance_manage/fittingEquation/fittingEquationList.vue
  46. 35 21
      src/views/chartRelevance_manage/mixins/classifyMixin.js
  47. 4 4
      src/views/chartRelevance_manage/relevance/list.vue
  48. 5 5
      src/views/chartRelevance_manage/relevance/relevanceChartEditor.vue
  49. 5 5
      src/views/chartRelevance_manage/statistic/statisticFeatureChartEditor.vue
  50. 3 3
      src/views/chartRelevance_manage/statistic/statisticFeatureList.vue
  51. 2 1
      src/views/dataEntry_manage/addChart.vue
  52. 8 2
      src/views/dataEntry_manage/components/LegendEditDia.vue
  53. 8 2
      src/views/dataEntry_manage/components/SaveChartOther.vue
  54. 41 10
      src/views/dataEntry_manage/components/addTarget.vue
  55. 7 1
      src/views/dataEntry_manage/components/insertData.vue
  56. 46 12
      src/views/dataEntry_manage/components/setEnNameDia.vue
  57. 113 117
      src/views/dataEntry_manage/databaseComponents/addTargetDiaBase.vue
  58. 261 42
      src/views/dataEntry_manage/databaseComponents/computedDialog.vue
  59. 52 3
      src/views/dataEntry_manage/databaseComponents/util.js
  60. 8 5
      src/views/dataEntry_manage/databaseList.vue
  61. 2 1
      src/views/dataEntry_manage/editChart.vue
  62. 209 14
      src/views/dataEntry_manage/mixins/chartPublic.js
  63. 564 0
      src/views/dataEntry_manage/thirdBase/YyzxData.vue
  64. 795 0
      src/views/dataEntry_manage/thirdBase/ysTargetBase.vue
  65. 2 2
      src/views/datasheet_manage/components/SheetExcel.vue
  66. 15 2
      src/views/datasheet_manage/components/sheetClassifyDia.vue
  67. 1 0
      src/views/datasheet_manage/components/sheetListWrap.vue
  68. 7 1
      src/views/login_manage/EmailModel.vue
  69. 8 5
      src/views/login_manage/MobileModel.vue
  70. 1 1
      src/views/login_manage/modelMixins.js
  71. 15 1
      src/views/mychart_manage/components/chartDetailDia.vue
  72. 21 20
      src/views/mychart_manage/components/chooseChart.vue
  73. 84 8
      src/views/mychart_manage/index.vue
  74. 58 21
      src/views/operation_manage/AIQA/AIQA.vue
  75. 6 4
      src/views/operation_manage/AIQA/components/messageItem.vue
  76. 2 3
      src/views/operation_manage/AIQA/components/newWindowHint.vue
  77. 1 1
      src/views/operation_manage/AIQA/components/windowListItem.vue
  78. 116 1
      src/views/ppt_manage/mixins/mixins.js
  79. 107 18
      src/views/ppt_manage/mixins/pptMixins.js
  80. 1 0
      src/views/ppt_manage/newVersion/components/catalog/pptContent.vue
  81. 1 0
      src/views/ppt_manage/newVersion/components/catalog/pptContentEn.vue
  82. 25 2
      src/views/ppt_manage/newVersion/pptCatalog.vue
  83. 10 12
      src/views/ppt_manage/newVersion/pptEditor.vue
  84. 25 2
      src/views/ppt_manage/newVersion/pptEnCatalog.vue
  85. 23 12
      src/views/ppt_manage/newVersion/pptEnEditor.vue
  86. 19 7
      src/views/predictEdb_manage/addPredicEdb.vue
  87. 216 17
      src/views/predictEdb_manage/components/dynamicRingdiffer.vue
  88. 6 2
      src/views/predictEdb_manage/predictEdb.vue
  89. 1 0
      src/views/report_manage/addreportNew.vue
  90. 3 0
      src/views/report_manage/editChapterReport.vue
  91. 1 0
      src/views/report_manage/editreportNew.vue
  92. 47 25
      src/views/report_manage/mixins/messagePush.js
  93. 71 13
      src/views/report_manage/mixins/reportMixin.js
  94. 1 0
      src/views/report_manage/reportEn/reportEditor.vue
  95. 1 6
      src/views/sandbox_manage/common/edge.js
  96. 36 78
      src/views/sandbox_manage/common/events.js
  97. 3 5
      src/views/sandbox_manage/common/gragh.js
  98. 223 108
      src/views/sandbox_manage/common/mindmap.js
  99. 1 12
      src/views/sandbox_manage/common/node.js
  100. 158 274
      src/views/sandbox_manage/index_new_version.vue

+ 4 - 2
src/api/api.js

@@ -10,7 +10,8 @@ import {
   sciDataInterface,
   baiinfoInterface,
   nationalInterface,
-  databankInterface
+  databankInterface,
+  yongyiInterface
 } from './modules/thirdBaseApi';
 
 //手工指标 手工数据 手工数据权限
@@ -112,7 +113,8 @@ export {
   cloudDiskInterface,
   homeInterface,
   businessTripInterence,
-  reportVarietyENInterence
+  reportVarietyENInterence,
+  yongyiInterface
 };
 
 //老接口 研报 ppt等

+ 2 - 2
src/api/http.js

@@ -77,7 +77,7 @@ const cancelTimeoutUrlPost = ["/cloud_disk/resource/upload"];
 export default {
   post(url, data) {
     //post请求方式
-    let timeout = cancelTimeoutUrlPost.includes(url) ? 0 : 100000;
+    let timeout = cancelTimeoutUrlPost.includes(url) ? 0 : 600000;
     return axios({
       method: "post",
       url: url,
@@ -100,7 +100,7 @@ export default {
       baseURL: process.env.VUE_APP_API_ROOT,
       url: url,
       params: getData(data, url),
-      timeout: 100000,
+      timeout:600000,
       headers: {
         "Content-type": "application/x-www-form-urlencoded; charset=utf-8",
       },

+ 338 - 1
src/api/modules/chartRelevanceApi.js

@@ -635,7 +635,19 @@ export const statisticFeatureInterface = {
 
   /**
    * 设置英文
-   * @param {*} params  ChartInfoId ChartNameEn
+   * @param {*} params  ChartInfoId ChartNameEn 
+   * "TagList": [
+      {
+        "ChartTagId": 1,
+        "TagNameEn": "宏观en"
+      }
+    ],
+    "VarietyList": [
+      {
+        "ChartVarietyId": 2,
+        "VarietyNameEn": "甲醇en"
+      }
+    ]
    * @returns 
    */
   editChartEn: params => {
@@ -682,4 +694,329 @@ export const statisticFeatureInterface = {
     previewSpline: params => {
       return http.post('/line_feature/preview_cure',params)
     }
+}
+
+/* 跨品种分析 */
+export const crossVarietyInterface = {
+  /**
+   * 分类列表
+   * @param {*} params 
+   * @returns 
+   */
+  classifyList: params => {
+    return http.get('/cross_variety/classify/list',params)
+  },
+
+  /**
+   * 分类添加
+   * @param {*} params ChartClassifyName
+   * @returns 
+   */
+  classifyAdd: params => {
+    return http.post('/cross_variety/classify/add',params)
+  },
+
+  /**
+   * 分类编辑
+   * @param {*} params ChartClassifyName ChartClassifyId
+   * @returns 
+   */
+  classifyEdit: params => {
+    return http.post('/cross_variety/classify/edit',params)
+  },
+
+  /**
+   * 分类移动
+   * @param {*} params 
+   * "ClassifyId": 269,
+    "PrevClassifyId": 0,
+    "NextClassifyId": 227,
+   * @returns 
+   */
+  classifyMove: params => {
+    return http.post('/cross_variety/classify/move',params)
+  },
+
+   /**
+   * 图表拖动
+   * @param {*} params 
+   * "ChartInfoId": 844,
+    "PrevChartInfoId": 0,
+    "NextChartInfoId": 839,
+    "ChartClassifyId": 270
+   * @returns 
+   */
+    chartMove: params => {
+      return http.post('/cross_variety/chart_info/move',params)
+    },
+  
+    /**
+     * 获取图表列表
+     * @param {*} params 
+     * CurrentIndex PageSize ChartClassifyId Keyword IsShowMe
+     * @returns 
+     */
+    getChartList: params => {
+      return http.get('/cross_variety/chart_info/list',params)
+    },
+  
+    /**
+     * 图表详情
+     * @param {*} params  ChartInfoId
+     * @returns 
+     */
+    chartDetail: params => {
+      return http.get('/cross_variety/chart_info/detail',params)
+    },
+
+    /**
+     * 添加图表
+     * @param {*} params 
+     * "ChartName": "跨品种分析图标名称",
+      "TagX": 1,
+      "TagY": 2,
+      "CalculateValue": 6,
+      "CalculateUnit": "月",
+      "DateConfigList": [
+        {
+          "DateType": 2,
+          "Num": 60
+        }
+      ],
+      "VarietyList": [2,5]
+     * @returns 
+     */
+    chartAdd: params => {
+      return http.post('/cross_variety/chart_info/add',params)
+    },
+
+    /**
+     * 编辑图表
+     * @param {*} params 
+     * ChartInfoId
+     * "ChartName": "跨品种分析图标名称",
+      "TagX": 1,
+      "TagY": 2,
+      "CalculateValue": 6,
+      "CalculateUnit": "月",
+      "DateConfigList": [
+        {
+          "DateType": 2,
+          "Num": 60
+        }
+      ],
+      "VarietyList": [2,5]
+     * @returns 
+     */
+    chartEdit: params => {
+      return http.post('/cross_variety/chart_info/edit',params)
+    },
+
+    /**
+     * 删除图表
+     * @param {*} params ChartInfoId
+     * @returns 
+     */
+    chartDel: params => {
+      return http.post('/cross_variety/chart_info/delete',params)
+    },
+
+    /**
+     * 预览图表
+     * @param {*} params 
+     *  "TagX": 1,
+        "TagY": 2,
+        "CalculateValue": 6,
+        "CalculateUnit": "月",
+        "DateConfigList": [
+          {
+            "DateType": 1,
+            "Num": 0
+          }
+        ],
+        "VarietyList": [2,5]
+     * @returns 
+     */
+    chartPreview: params => {
+      return http.post('/cross_variety/chart_info/preview',params)
+    },
+
+    /**
+     * 设置英文
+     * @param {*} params  ChartInfoId ChartNameEn
+     * "TagList": [
+        {
+          "ChartTagId": 1,
+          "TagNameEn": "宏观en"
+        }
+      ],
+      "VarietyList": [
+        {
+          "ChartVarietyId": 2,
+          "VarietyNameEn": "甲醇en"
+        }
+      ]
+     */
+    editChartEn: params => {
+      return http.post('/cross_variety/chart_info/en/edit',params)
+    },
+
+    /**
+     * 图表搜索
+     * @param {*} params Keyword
+     * @returns 
+     */
+    searchChart: params => {
+      return http.get('/cross_variety/chart_info/search_by_es',params)
+    },
+
+    /**
+     * 刷新图表
+     * @param {*} params ChartInfoId
+     * @returns 
+     */
+    refreshChart: params => {
+      return http.get('/cross_variety/chart_info/refresh',params)
+    },
+
+    /**
+     * 图表另存为
+     * @param {*} params ChartInfoId ChartName
+     * @returns 
+     */
+    saveOtherChart: params => {
+      return http.post('/cross_variety/chart_info/copy',params)
+    },
+    
+    /**
+     * 编辑英文名称
+     * @param {*} params 
+     * @returns 
+     */
+    editChartEn: params => {
+      return http.post('/cross_variety/chart_info/en/edit',params)
+    },
+
+    /**
+     * 图表设置语言配置信息
+     * @param {*} params  ChartInfoId
+     * @returns 
+     */
+    chartLangOption: params => {
+      return http.get('/cross_variety/chart_info/relation',params)
+    },
+    
+    /**
+     * 品种列表
+     * @param {*} params 
+     */
+    getVarietyList: params => {
+      return http.get('/cross_variety/variety/list',params)
+    },
+
+    /**
+     * 添加品种
+     * @param {VarietyName} params 
+     * @returns 
+     */
+    varietyAdd: params => {
+      return http.post('/cross_variety/variety/add',params)
+    },
+    
+    /**
+     * 编辑品种
+     * @param {VarietyName ChartVarietyId} params 
+     * @returns 
+     */
+    varietyEdit: params => {
+      return http.post('/cross_variety/variety/edit',params)
+    },
+    
+    /**
+     * 删除品种检测
+     * @param {ChartVarietyId} params 
+     * @returns 
+     */
+    varietyDelCheck: params => {
+      return http.post('/cross_variety/variety/delete/check',params)
+    },
+    
+    /**
+     * 删除品种
+     * @param {ChartVarietyId} params 
+     * @returns 
+     */
+    varietyDel: params => {
+      return http.post('/cross_variety/variety/delete',params)
+    },
+
+    /**
+     * 标签列表
+     * @param {*} params 
+     */
+    getTagList: params => {
+      return http.get('/cross_variety/tag/list',params)
+    },
+
+    /**
+     * 添加标签
+     * @param {TagName} params 
+     * @returns 
+     */
+    tagAdd: params => {
+      return http.post('/cross_variety/tag/add',params)
+    },
+
+    /**
+     * 编辑标签
+     * @param {TagName ChartTagId} params 
+     * @returns 
+     */
+    tagEdit: params => {
+      return http.post('/cross_variety/tag/edit',params)
+    },
+
+    /**
+     * 删除标签检测
+     * @param {ChartTagId} params 
+     * @returns 
+     */
+    tagDelCheck: params => {
+      return http.post('/cross_variety/tag/delete/check',params)
+    },
+
+    /**
+     * 添加标签
+     * @param {ChartTagId} params 
+     * @returns 
+     */
+    tagDel: params => {
+      return http.post('/cross_variety/tag/delete',params)
+    },
+
+    /**
+     * 设置标签关联品种对应信息
+     * @param {
+        "ChartTagId": 1,
+        "VarietyEdb": [
+            {
+                "ChartVarietyId": 2,
+                "EdbInfoId": 103407
+            },
+        ]
+     * } params 
+     * @returns 
+     */
+    setTagRelationVariety: params => {
+      return http.post('/cross_variety/tag/variety_edb/save',params)
+    },
+
+
+    /**
+     * 获取标签关联的品种信息
+     * @param {ChartTagId} params 
+     */
+    getTagRelation: params => {
+      return http.get('/cross_variety/tag/variety_edb/list',params)
+    }
 }

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

@@ -256,6 +256,16 @@ export default{
      */
     transCatalogtoEn:params=>{
       return http.post('pptv2/batchToEn',params)
+    },
+    /**
+     * PPT编辑加锁
+     * @param {*} params 
+     * @param {Number} params.PptId 
+     * @param {Number} params.Status :0仅查看,1编辑中,2编辑完成
+     * @returns 
+     */
+    pptEditLock:params=>{
+        return http.post('pptv2/editing',params)
     }
 
 }

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

@@ -217,6 +217,16 @@ export const pptEnInterface = {
      */
     transPPTtoReport:params => {
       return http.post('/ppt_english/toReport',params)
+    },
+    /**
+     * PPT编辑加锁
+     * @param {*} params 
+     * @param {Number} params.PptId 
+     * @param {Number} params.Status :0仅查看,1编辑中,2编辑完成
+     * @returns 
+     */
+    pptEditLock:params=>{
+        return http.post('ppt_english/editing',params)
     }
 
 }

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

@@ -287,6 +287,27 @@ export const getMixedCalculateData = params => {
 	return http.post('/datamanage/excel_info/mixed/calculate',params)
 }
 
+/**
+ * 表格一键刷新
+ * @param {Object} params
+ * @param {Array} params.ExcelCodes 表格唯一编码
+ * @param {String} params.Source 来源,枚举值:report、english_report、smart_report
+ * @param {Number} params.ReportId 报告id
+ * @param {Number} params.ReportChapterId 章节id 非章节传0
+ */
+export const refreshSheet = (params)=>{
+    return http.post('/datamanage/excel_info/table/batch_refresh',params)
+}
+/**
+ * 获取表格刷新结果
+ * @param {Object} params
+ * @param {String} params.Source 来源,枚举值:report、english_report、smart_report
+ * @param {Number} params.ReportId 报告id
+ * @param {Number} params.ReportChapterId 章节id 非章节传0
+ */
+export const getRefreshResult = (params)=>{
+    return http.post('/datamanage/excel_info/table/batch_refresh/result',params)
+}
 
 /* =====自定义分析==== */
 

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

@@ -76,6 +76,26 @@ const apiSmartReport={
     //获取上期已发布报告
     getLastReport:params=>{
         return http.get('/smart_report/last_published_report',params)
+    },
+
+    // 资源库列表
+    imgReourceList:params=>{
+        return http.get('/smart_report/resource/list',params)
+    },
+
+    //新增资源库图片
+    imgReourceAdd:params=>{
+        return http.post('/smart_report/resource/add',params)
+    },
+
+    //资源库图片重命名
+    imgReourceRename:params=>{
+        return http.post('/smart_report/resource/rename',params)
+    },
+
+    //资源库图片删除
+    imgReourceDel:params=>{
+        return http.post('/smart_report/resource/remove',params)
     }
 
 }

+ 60 - 2
src/api/modules/thirdBaseApi.js

@@ -186,7 +186,38 @@ const smmDataInterface = {
 	 */
   cleanClassify:params=>{
     return http.post('/datamanage/smm/reset',params)
-  }
+  },
+	// 有色原始数据库
+	/**
+   * 列表
+	*/
+	getYsDataList:params=>{
+		return http.get('datamanage/smm/api/list',params)
+	},
+	/**
+   * 分类列表
+	*/
+	getYsTypeDataList:params=>{
+		return http.get('datamanage/smm/api/type/list',params)
+	},
+	/**
+   * 检验有色指标是否存在
+	*/
+	getYsEdbCodeCheck:params=>{
+		return http.get('datamanage/edb_info/smm/exist/check',params)
+	},
+	/**
+   * 查询有色指标
+	*/
+	getYsEdbCodeSearch:params=>{
+		return http.get('datamanage/edb_info/smm/search',params)
+	},
+	/**
+   * 批量添加有色指标
+	*/
+	ysEdbAddBatch:params=>{
+		return http.post('datamanage/edb_info/smm/batch/add',params)
+	},
 }
 
 /* 中国煤炭网 */
@@ -720,6 +751,32 @@ const databankInterface = {
     },
 }
 
+/* 涌益咨询 */
+const yongyiInterface={
+	/**
+	 * 分类列表
+	 * @param {} params 
+	 * @returns 
+	 */
+	classifyList: params => {
+		return http.get('/datamanage/yongyi/classify',params);
+	},
+	/**
+	 * 获取指标列表详情
+	 */
+	dataList: params => {
+		return http.get('/datamanage/yongyi/index/data',params);
+	},
+	//单个指标数据
+	getTargetDataList:params=>{
+		return http.get('/datamanage/yongyi/single_data',params);
+	},
+	// 搜索
+	getTargetListByName:params=>{
+		return http.get('/datamanage/yongyi/search_list',params);
+	},
+}
+
 export { 
 	lzDataInterface,
 	glDataInterface,
@@ -730,5 +787,6 @@ export {
   sciDataInterface,
   baiinfoInterface,
   nationalInterface,
-  databankInterface
+  databankInterface,
+  yongyiInterface
 }

BIN
src/assets/img/icons/add_blue_new.png


BIN
src/assets/img/icons/ai_Company.png


BIN
src/assets/img/icons/ai_avatar.png


BIN
src/assets/img/icons/fullsreen.png


BIN
src/assets/img/icons/gpt-4-turbo.png


BIN
src/assets/img/icons/gpt-4.png


BIN
src/assets/img/icons/more.png


BIN
src/assets/img/smartReport/icon17.png


BIN
src/assets/img/smartReport/icon18.png


+ 22 - 10
src/components/chart/chartDetailHandlesWrap.vue

@@ -162,20 +162,32 @@ export default {
             enNameSetting:checkPermissionBtn(productPricePermission.goodsPrice_incomeLine_enNameSetting),
             del:checkPermissionBtn(productPricePermission.goodsPrice_incomeLine_del),
         }
-        //相关性分析
-        if(path==='/chartrelevance'){
-            return chartrelevanceMap[btn]
+
+         const crossVarietyBtnMap = {
+            share:checkPermissionBtn(statisticPermission.crossVariety_share),
+            addMyChart:checkPermissionBtn(statisticPermission.crossVariety_addMyChart),
+            refresh:checkPermissionBtn(statisticPermission.crossVariety_refresh),
+            otherSave:checkPermissionBtn(statisticPermission.crossVariety_otherSave),
+            edit:checkPermissionBtn(statisticPermission.crossVariety_edit),
+            copyOffice:checkPermissionBtn(statisticPermission.crossVariety_copyOffice),
+            copyWechat:checkPermissionBtn(statisticPermission.crossVariety_copyWechat),
+            enNameSetting:checkPermissionBtn(statisticPermission.crossVariety_enNameSetting),
+            del:checkPermissionBtn(statisticPermission.crossVariety_del),
         }
+        //相关性分析
+        if(path==='/chartrelevance') return chartrelevanceMap[btn]
+
         //拟合方程曲线
-        if(path==='/fittingEquationList'){
-            return fittingEquationMap[btn]
-        }
+        else if(path==='/fittingEquationList') return fittingEquationMap[btn]
+        
         //统计特性
-        if(path==='/statisticFeatureList'){
-            return statisticFeatureMap[btn]
-        }
+        else if(path==='/statisticFeatureList') return statisticFeatureMap[btn]
+
+        //跨品种分析
+        else if(path==='/crossVarietyChartList') return crossVarietyBtnMap[btn]
+
         //商品价格曲线
-        if(path==='/commordityChartBase'){
+        else if(path==='/commordityChartBase'){
             const {Source,ChartType} = this.chartInfo
             if(Source===2&&ChartType===8){//是商品价格曲线
                 return priceLineMap[btn]

+ 7 - 3
src/components/chart/chartListWrap.vue

@@ -67,15 +67,19 @@ export default {
             return checkPermissionBtn(statisticPermission.corrAnalysis_addMyChart)
         }
         //拟合方程曲线
-        if(path==='/fittingEquationList'){
+        else if(path==='/fittingEquationList'){
             return checkPermissionBtn(statisticPermission.fittingEq_addMyChart)
         }
         //统计特性
-        if(path==='/statisticFeatureList'){
+        else if(path==='/statisticFeatureList'){
             return checkPermissionBtn(statisticPermission.statisticFeature_addMyChart)
         }
+        //跨品种分析
+        else if(path==='/crossVarietyChartList') {
+          return checkPermissionBtn(statisticPermission.crossVariety_addMyChart)
+        }
         //商品价格曲线
-        if(path==='/commordityChartBase'){
+        else if(path==='/commordityChartBase'){
             const {Source,ChartType} = item
             if(Source===2&&ChartType===8){//是商品价格曲线
                 return checkPermissionBtn(productPricePermission.goodsPrice_priceLine_addMyChart)

+ 130 - 42
src/components/lzTable.vue

@@ -65,19 +65,107 @@ export default {
 			defalut:'lz'
 		}
 	},
+	computed: {
+		headerArr(){
+			let arr=['QuotaName','LzCode','Frequency','UnitName','ModifyTime']
+
+			if(this.source==='gl'){
+				arr=['IndexName','IndexCode','FrequencyName','UnitName','UpdateTime']
+			}
+			if(this.source==='smm'){
+				arr=['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+			}
+			if(this.source==='coal'){
+				arr=['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+			}
+			if(this.source==='baiinfo'){
+				arr=['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+			}
+			if(this.source==='yyzx'){
+				arr=['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+			}
+
+			return arr
+		},
+		labelArr(){
+			let temMap=new Map([
+				['QuotaName', '指标名称'],
+				['LzCode', '指标ID'],
+				['Frequency', '频度'],
+				['UnitName', '单位'],
+				['ModifyTime', '更新时间'],
+			])
+
+			if(this.source==='gl'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['FrequencyName', '频度'],
+					['UnitName', '单位'],
+					['UpdateTime', '更新时间'],
+				])
+			}
+			if(this.source==='smm'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['Frequency', '频度'],
+					['Unit', '单位'],
+					['ModifyTime', '更新时间'],
+				])
+			}
+			if(this.source==='coal'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['Frequency', '频度'],
+					['Unit', '单位'],
+					['ModifyTime', '更新时间'],
+				])
+			}
+			if(this.source==='baiinfo'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['Frequency', '频度'],
+					['Unit', '单位'],
+					['ModifyTime', '更新时间'],
+				])
+			}
+			if(this.source==='yyzx'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['Frequency', '频度'],
+					['Unit', '单位'],
+					['ModifyTime', '更新时间'],
+				])
+			}
+
+			return temMap
+		},
+		dynamic_key(){
+			let key='InputValue'
+			if(['smm','baiinfo','coal','yyzx'].includes(this.source)){
+				key='Value'
+			}
+
+			return key
+		}
+	},
 	data() {
 		return {
-			dynamic_key: (this.source === 'smm'||this.source ==='baiinfo')? 'Value' 
-			:this.source==='coal'?'Value': 'InputValue',
-			headerArr: this.source === 'gl' 
-			? ['IndexName','IndexCode','FrequencyName','UnitName','UpdateTime']
-			: this.source === 'smm' 
-			? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
-			: this.source==='coal'
-			? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
-      : this.source==='baiinfo'
-      ? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
-			:['QuotaName','LzCode','Frequency','UnitName','ModifyTime'],
+			// dynamic_key: (this.source === 'smm'||this.source ==='baiinfo')? 'Value' 
+			// :this.source==='coal'?'Value': 'InputValue',
+	// 		headerArr: this.source === 'gl' 
+	// 		? ['IndexName','IndexCode','FrequencyName','UnitName','UpdateTime']
+	// 		: this.source === 'smm' 
+	// 		? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+	// 		: this.source==='coal'
+	// 		? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+    //   : this.source==='baiinfo'
+    //   ? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+	// 		:['QuotaName','LzCode','Frequency','UnitName','ModifyTime'],
 			frequencyType:new Map([
 				[1,'日度'],
 				[2,'周度'],
@@ -86,37 +174,37 @@ export default {
 				[5,'年度'],
 				[99,'无固定频率'],
 			]),
-			labelArr: this.source === 'gl' ? new Map([
-				['IndexName', '指标名称'],
-				['IndexCode', '指标ID'],
-				['FrequencyName', '频度'],
-				['UnitName', '单位'],
-				['UpdateTime', '更新时间'],
-			]): this.source === 'smm' ? new Map([
-				['IndexName', '指标名称'],
-				['IndexCode', '指标ID'],
-				['Frequency', '频度'],
-				['Unit', '单位'],
-				['ModifyTime', '更新时间'],
-			]) :this.source === 'coal' ? new Map([
-				['IndexName', '指标名称'],
-				['IndexCode', '指标ID'],
-				['Frequency', '频度'],
-				['Unit', '单位'],
-				['ModifyTime', '更新时间'],
-			]) :this.source==='baiinfo'?new Map([
-        ['IndexName', '指标名称'],
-				['IndexCode', '指标ID'],
-				['Frequency', '频度'],
-				['Unit', '单位'],
-				['ModifyTime', '更新时间'],
-      ]):new Map([
-				['QuotaName', '指标名称'],
-				['LzCode', '指标ID'],
-				['Frequency', '频度'],
-				['UnitName', '单位'],
-				['ModifyTime', '更新时间'],
-			]),
+			// labelArr: this.source === 'gl' ? new Map([
+			// 	['IndexName', '指标名称'],
+			// 	['IndexCode', '指标ID'],
+			// 	['FrequencyName', '频度'],
+			// 	['UnitName', '单位'],
+			// 	['UpdateTime', '更新时间'],
+			// ]): this.source === 'smm' ? new Map([
+			// 	['IndexName', '指标名称'],
+			// 	['IndexCode', '指标ID'],
+			// 	['Frequency', '频度'],
+			// 	['Unit', '单位'],
+			// 	['ModifyTime', '更新时间'],
+			// ]) :this.source === 'coal' ? new Map([
+			// 	['IndexName', '指标名称'],
+			// 	['IndexCode', '指标ID'],
+			// 	['Frequency', '频度'],
+			// 	['Unit', '单位'],
+			// 	['ModifyTime', '更新时间'],
+			// ]) :this.source==='baiinfo'?new Map([
+        	// 	['IndexName', '指标名称'],
+			// 	['IndexCode', '指标ID'],
+			// 	['Frequency', '频度'],
+			// 	['Unit', '单位'],
+			// 	['ModifyTime', '更新时间'],
+      		// ]):new Map([
+			// 	['QuotaName', '指标名称'],
+			// 	['LzCode', '指标ID'],
+			// 	['Frequency', '频度'],
+			// 	['UnitName', '单位'],
+			// 	['ModifyTime', '更新时间'],
+			// ]),
 		};
 	},
 	methods: {

+ 21 - 7
src/routes/modules/chartRoutes.js

@@ -229,17 +229,17 @@ export default [
 			{
 				path: 'chartrelevance',
 				name: '相关性分析',
-				component:()=>import('@/views/chartRelevance_manage/list.vue')
+				component:()=>import('@/views/chartRelevance_manage/relevance/list.vue')
 			},
 			{
 				path: 'fittingEquationList',
 				name: '拟合方程曲线',
-				component:()=>import('@/views/chartRelevance_manage/fittingEquationList.vue')
+				component:()=>import('@/views/chartRelevance_manage/fittingEquation/fittingEquationList.vue')
 			},
 			{
 				path: 'fittingEquationChartEditor',
 				name: '添加图表',
-				component: () => import('@/views/chartRelevance_manage/fittingEquationChartEditor.vue'),
+				component: () => import('@/views/chartRelevance_manage/fittingEquation/fittingEquationChartEditor.vue'),
 				meta: { 
 					pathFrom: "fittingEquationList",
           			pathName: "拟合方程曲线",
@@ -248,7 +248,7 @@ export default [
 			{
 				path: 'relevancechartEditor',
 				name: '编辑图表',
-				component:()=>import('@/views/chartRelevance_manage/relevanceChartEditor.vue'),
+				component:()=>import('@/views/chartRelevance_manage/relevance/relevanceChartEditor.vue'),
 				meta: { 
 					pathFrom: "chartrelevance",
           			pathName: "相关性图表",
@@ -257,17 +257,31 @@ export default [
 			{
 				path: 'statisticFeatureList',
 				name: '统计特征',
-				component: () => import('@/views/chartRelevance_manage/statisticFeatureList.vue')
+				component: () => import('@/views/chartRelevance_manage/statistic/statisticFeatureList.vue')
 			},
 			{
 				path: 'statisticFeatureChartEditor',
 				name: '编辑图表',
-				component:()=>import('@/views/chartRelevance_manage/statisticFeatureChartEditor.vue'),
+				component:()=>import('@/views/chartRelevance_manage/statistic/statisticFeatureChartEditor.vue'),
 				meta: { 
 					pathFrom: "statisticFeatureList",
           			pathName: "统计特征",
 				}
-			}
+			},
+			{
+				path: 'crossVarietyChartList',
+				name: '跨品种分析',
+				component: () => import('@/views/chartRelevance_manage/crossVarietyAnalysis/list.vue')
+			},
+			{
+				path: 'crossVarietyChartEditor',
+				name: '编辑图表',
+				component:()=>import('@/views/chartRelevance_manage/crossVarietyAnalysis/chartEditor.vue'),
+				meta: { 
+					pathFrom: "crossVarietyChartList",
+          pathName: "跨品种分析",
+				}
+			},
 		]
 	},
 

+ 16 - 0
src/routes/modules/dataRoutes.js

@@ -201,6 +201,22 @@ export default [
           keepAlive: false,
         },
       },
+      {
+        path: "yyzx",
+        component: () => import("@/views/dataEntry_manage/thirdBase/YyzxData.vue"),
+        name: "涌益咨询",
+        hidden: false,
+        meta: {
+          keepAlive: false,
+        },
+      },
+      {
+        path: "ysTarget",
+        component: () =>
+          import("@/views/dataEntry_manage/thirdBase/ysTargetBase.vue"),
+        name: "有色原始数据库",
+        hidden: false,
+      },
     ],
   },
 ];

+ 21 - 2
src/utils/buttonConfig.js

@@ -42,7 +42,7 @@ export const reportManageBtn={
     reportManage_reportView:'smartReportManage:reportView',//研报预览:即是否能点击研报名称跳转预览页面
     reportManage_reportView_wechartShare:'smartReportManage:reportView:wechartShare',//研报预览页面-微信分享
     reportManage_reportView_copyWechat:'smartReportManage:reportView:copyWechat',//研报预览页面-复制链接
-    reportManage_reportView_exportImg:'smartReportManage:reportView:exportImg',//研报预览页面-导出图片
+    reportManage_exportImg:'smartReportManage:exportImg',//导出图片
     reportManage_audioDownload:'smartReportManage:audioDownload',//音频下载
     reportManage_audioUpload:'smartReportManage:audioUpload',//音频上传
     reportManage_reportDel:'smartReportManage:reportDel',//删除研报
@@ -233,12 +233,13 @@ export const dataSourcePermission = {
     eiaData_export:'eiaData:export',
     /*--------国家统计局---- */
     gjtjjData_export:'gjtjjData:export',
-
     /*--------数据报表管理---- */
     /*--------数据源终端管理---- */
     dataSource_account_add:'dataSourceAccount:add',//新增终端账号
     dataSource_account_edit:'dataSourceAccount:edit',//编辑
     dataSource_account_enable:'dataSourceAccount:enable',//禁用/启用
+    /*--------涌益咨询---- */
+    yyzxData_export:'yyzx:exportData'
 }
 
 /*
@@ -513,6 +514,24 @@ export const statisticPermission = {
     fittingEq_refresh:'fittingEq:refresh',
     fittingEq_addMyChart:'fittingEq:addMyChart',
     fittingEq_share:'fittingEq:share',
+
+    /* 跨品种分析 */
+    crossVariety_addChart:'crossVariety:addChart',//添加图表按钮
+    crossVariety_onlyMine:'crossVariety:onlyMine',//只看我的
+    crossVariety_classifyOpt_edit:'crossVariety:classifyOpt:edit',//添加编辑分类
+    crossVariety_classifyOpt_delete:'crossVariety:classifyOpt:delete',//删除分类
+    crossVariety_variety_edit: 'crossVariety:variety:edit',//品种管理
+    crossVariety_tag_edit: 'crossVariety:tag:edit',//标签管理
+        /*---图表操作栏--- */
+    crossVariety_del:'crossVariety:del',
+    crossVariety_enNameSetting:'crossVariety:enNameSetting',
+    crossVariety_copyWechat:'crossVariety:copyWechat',
+    crossVariety_copyOffice:'crossVariety:copyOffice',
+    crossVariety_edit:'crossVariety:edit',
+    crossVariety_otherSave:'crossVariety:otherSave',
+    crossVariety_refresh:'crossVariety:refresh',
+    crossVariety_addMyChart:'crossVariety:addMyChart',
+    crossVariety_share:'crossVariety:share',
 }
 /*
  * --------------------------------------------------------------------------供应分析------------------------------------------------

+ 1 - 0
src/utils/parseData.js

@@ -2,6 +2,7 @@ import ParserData from '@/api/crypto.js';
 /* 解密数据处理 */
 export function parseData(response) {
   const headKeyStr=response.headers.dk
+  sessionStorage.setItem('dk',headKeyStr);
   const desKey=ParserData.Des3Decrypt(headKeyStr,'JMCqSoUrTAmyNNIRb0TtlrPk')
   let result = process.env.NODE_ENV == "production"
   ? JSON.parse(ParserData.Des3Decrypt(response.data,desKey))

+ 8 - 1
src/utils/svgToblob.js

@@ -50,7 +50,14 @@ export function copyFit(value) {
     navigator.clipboard.writeText(value)
   }else {
     setTimeout(() => {
-      bus.$copyText(value)
+      const input = document.createElement('input')
+      input.setAttribute('readonly','readonly')
+      input.setAttribute('id',new Date().getTime())
+      input.value = value
+      document.body.appendChild(input)
+      input.select();
+      document.execCommand('copy');
+      document.body.removeChild(input);
     })
   }
 

+ 11 - 1
src/views/Home.vue

@@ -105,7 +105,7 @@
                       :style="`display: flex;align-items:center;color:${
                         child.path === activePath ? $setting.theme_color : '#666'
                       }`"
-                      @click="(e) => e.preventDefault()"
+                      @click="handleClickSubMenuItem(child,$event)"
                     >
                       <svg t="1689672247666" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7676" width="30" height="30"><path d="M640 499.2v25.6a21.333333 21.333333 0 0 1-21.333333 21.333333h-213.333334a21.333333 21.333333 0 0 1-21.333333-21.333333v-25.6a21.333333 21.333333 0 0 1 21.333333-21.333333h213.333334a21.333333 21.333333 0 0 1 21.333333 21.333333z"  p-id="7677" :fill="child.path === activePath ? $setting.theme_color : '#666'"></path></svg>
                       <span style="margin-left:15px;">{{ child.name }}</span>
@@ -495,6 +495,16 @@ export default {
     this.$refs.notification&&this.$refs.notification.getMsgList()
   },
   methods: {
+    handleClickSubMenuItem(item,e){
+      e.preventDefault();
+      if(item.path==='AIQA'){
+        e.stopPropagation();
+        let { href } = this.$router.resolve({
+          path:'/'+item.path
+        });
+        window.open(href, '_blank')
+      }
+    },
     // 获取商家Code
     getBusinessCodeFun(){
       getBusinessCode().then(res=>{

+ 12 - 12
src/views/Login.vue

@@ -247,15 +247,15 @@ export default {
                                 case "researcher":
                                     path = "/reportlist";
                                     break;
-                                case "compliance": //合规
-                                    path = "/contractapprovallist";
-                                    break;
+                                // case "compliance": //合规
+                                //     path = "/contractapprovallist";
+                                //     break;
                                 case "special_researcher": //特邀研究员
                                     path = "/dataList";
                                     break;
-                                case "special_ficc_seller":
-                                    path = "/meetingCalendar";
-                                    break;
+                                // case "special_ficc_seller":
+                                //     path = "/meetingCalendar";
+                                //     break;
                                 default:
                                     path = await this.getOtherRolePath("myCalendar");
                             }
@@ -474,15 +474,15 @@ export default {
                 case "researcher":
                     path = "/reportlist";
                     break;
-                case "compliance": //合规
-                    path = "/contractapprovallist";
-                    break;
+                // case "compliance": //合规
+                //     path = "/contractapprovallist";
+                //     break;
                 case "special_researcher": //特邀研究员
                     path = "/dataList";
                     break;
-                case "special_ficc_seller":
-                    path = "/meetingCalendar";
-                    break;
+                // case "special_ficc_seller":
+                //     path = "/meetingCalendar";
+                //     break;
                 default:
                     path = await this.getOtherRolePath("myCalendar");
             }

+ 19 - 0
src/views/chartFrame_manage/common/config.js

@@ -10,6 +10,25 @@ const portStyle = {
         }
     }
     }
+//利用NodeTool实现角标
+export const textTool = {
+    name:'button',
+    args:{
+        x:'100%',
+        y:'100%',
+        offset:{x:-12,y:-5},
+        markup:[{
+            tagName:'text',
+            textContent:'aaa',
+            attrs:{
+                fill: '#0052D9',
+                fontSize: 12,
+                textAnchor: 'middle',
+                pointerEvents: 'none',
+            }
+        }]
+    }
+}
 //基础节点
 export const baseNode = {
     shape:'rect',

+ 1 - 5
src/views/chartFrame_manage/common/graph.js

@@ -9,13 +9,9 @@ const viewConfig = {
         restrict:true,//节点移动时无法超出画布
     },
     interacting:function (cellView){ //禁止节点移动
-        /* if(cellView.cell.getData().disableMove){
-            return false
-        } */
-        return false
+        return {'nodeMovable':false,'edgeMovable':false}
     },
     highlighting:{},
-    /* connecting:{}, */
     history:false,//关闭画布撤销/重做能力。
     keyboard:false,
     clipboard: false,

+ 31 - 6
src/views/chartFrame_manage/components/frameContainer.vue

@@ -32,7 +32,7 @@
 <script>
 import { ElDropdownMenu } from 'element-ui';
 import { myGraph } from '../common/graph';
-import { baseNode } from '../common/config';
+import { baseNode , textTool } from '../common/config';
 import FrameToolBar from './frameToolBar.vue';
 export default {
     components:{ElDropdownMenu,FrameToolBar},
@@ -40,6 +40,10 @@ export default {
         FrameworkContent:{ //框架内容
             type:String,
             default:''
+        },
+        Nodes:{
+            type:Array,
+            default:[]
         }
     },
     data() {
@@ -70,9 +74,6 @@ export default {
                 this.currentCell = null
             }
         },
-        FrameworkContent(newVal){//当框架内容发生改变时,画布内容也发生改变
-            newVal.length&&this.gragh&&this.graph.fromJSON(JSON.parse(newVal))
-        }
     },
     computed:{
         contextMenu(){//右键菜单,根据权限配置
@@ -95,6 +96,8 @@ export default {
             this.FrameworkContent.length&&this.graph.fromJSON(JSON.parse(this.FrameworkContent))
             //如果有内容,将画布内容居中
             this.FrameworkContent.length&&this.graph.scrollToContent({ animation: { duration: 600 }})
+            //如果有内容,遍历每个节点,赋值chartNum
+            this.FrameworkContent.length&&this.setNodeInfo()
             //如果是非编辑页,加载完成画布内容后冻结画布
             window.location.pathname.startsWith('/chartframe')&&this.graph.freeze()
             //如果是编辑页,加载完成后清除历史数据
@@ -104,6 +107,17 @@ export default {
         dispose(){
             this.graph&&this.graph.dispose()
         },
+        setNodeInfo(){
+            const nodes = this.graph.getNodes()
+            this.Nodes.forEach(node=>{
+                const currentNode = nodes.find(item=>item.id===node.NodeId)
+                if(currentNode){
+                    currentNode.removeTools()
+                    const toolOption = this.getToolOption({chartNum:node.ChartNum||0,color:currentNode.attrs.label.fill})
+                    currentNode.addTools(toolOption)
+                }
+            })
+        },
         //添加/编辑节点
         editNode(node){
             //获取视口范围
@@ -114,6 +128,9 @@ export default {
                 currentNode.data.id=node.nodeLink.MyChartClassifyId
                 currentNode.label=node.nodeName
                 currentNode.data.nodeLink = node.nodeLink
+                currentNode.removeTools()
+                const toolOption = this.getToolOption({chartNum:node.nodeLink.ChartNum,color:currentNode.attrs.label.fill})
+                currentNode.addTools(toolOption)
             }else{
                 //在视口范围内添加节点
                 this.graph.addNode({
@@ -125,12 +142,19 @@ export default {
                     height:50,
                     data:{
                         id:node.nodeLink.MyChartClassifyId,//存储节点对应的myETA分类id
-                        nodeLink:node.nodeLink
+                        nodeLink:node.nodeLink,
                     },
-                    label:node.nodeName||''
+                    label:node.nodeName||'',
+                    tools:[this.getToolOption({chartNum:node.nodeLink.ChartNum,color:baseNode.attrs.label.fill})]
                 }})
             }
         },
+        getToolOption({chartNum,color}){
+            const options = _.cloneDeep(textTool)
+            options.args.markup[0].textContent = chartNum +''
+            options.args.markup[0].attrs.fill = color
+            return options
+        },
         //点击右键菜单事件
         handleContext(key){
             const select_cell = this.graph.getSelectedCells()
@@ -191,6 +215,7 @@ export default {
         getContentNodes(){
             return this.graph.getNodes().map(node=>{
                 return {
+                    NodeId:node.id,
                     NodeName:node.label,
                     MyChartClassifyId:Number(node.data.id)
                 }

+ 9 - 1
src/views/chartFrame_manage/components/frameToolBar.vue

@@ -115,7 +115,7 @@
                             :class="{'img-disabled':!isSelectNode&&!isSelectEdge,'actived':isSelectNode||isSelectEdge}">
                     </label>
                     <input type="color" id="storke" style="width: 0;height: 0;visibility: hidden;" 
-                        :value="cellStyle.stroke"
+                        :value="nodeStyle.stroke"
                         @input="valueChange"/>
                     <span class="disabled" v-if="!isSelectNode&&!isSelectEdge"></span>
                 </span>
@@ -311,7 +311,15 @@ export default {
                 if(styleMap[id]){
                     attr = this.isSelectNode?styleMap[id][0]:styleMap[id][1]
                 } 
+                
                 this.currentCell.attr(attr,value)
+                //改变节点字体颜色时,角标的颜色也一起改变
+                if(attr==='label/fill'){
+                    let tool = this.currentCell.getTools().items[0]
+                    tool.args.markup[0].attrs.fill = value
+                    this.currentCell.removeTools()
+                    this.currentCell.addTools(tool)
+                }
             }
         },
         changeCellStyle({attr,value}){

+ 9 - 1
src/views/chartFrame_manage/css/basePage.scss

@@ -57,12 +57,20 @@
                 overflow-y: auto;
             } */
             .public-catalog{
+                .title{
+                    cursor: pointer;
+                    span{
+                        margin-left: 5px;
+                    }
+                }
                 .catalog-tree{
-                    margin: 20px 0;
+                   /*  margin: 20px 0; */
+                   margin-top:20px;
                     min-height: 100px;
                 }
             }
             .my-list{
+                margin-top: 20px;
                 .classify-item{
                     display: flex;
                     align-items: center;

+ 1 - 1
src/views/chartFrame_manage/css/customTree.scss

@@ -1,4 +1,4 @@
-.chart-frame-wrap{
+.chart-frame-wrap,.tree-wrap{
     .catalog-tree{
         .custom-tree-node {
             display: flex !important;

+ 14 - 2
src/views/chartFrame_manage/frameEditor.vue

@@ -3,7 +3,8 @@
     <div class="frame-editor-wrap">
         <div class="option-wrap">
             <el-input style="width:240px;" placeholder="请输入框架名称" v-model.trim="frameDetail.FrameworkName"></el-input>
-            <el-button type="primary" style="margin-left:auto;" @click="handleEditNode({})"
+            <el-button type="primary" plain style="margin-left:auto;" @click="returnListPage">返回</el-button>
+            <el-button type="primary" style="margin-left:20px;" @click="handleEditNode({})"
                 v-if="permissionBtn.isShowBtn('chartFramePermission','chartframe_my_editNode')">添加节点</el-button>
             <el-button type="primary" style="margin-left:20px;" @click="saveFrame"
                 v-if="permissionBtn.isShowBtn('chartFramePermission','chartframe_my_saveFrame')">保存</el-button>
@@ -12,6 +13,7 @@
             <!-- 沙盘图组件 -->
             <FrameContainer ref="container"
                 :FrameworkContent="frameDetail.FrameworkContent"
+                :Nodes="frameDetail.Nodes"
                 @editNode="handleEditNode"
                 @framePic="getFramePic"
             />
@@ -70,6 +72,7 @@ export default {
             frameDetail:{
                 FrameworkName:'',
                 FrameworkContent:'',
+                Nodes:[]
             },
             lockLoding:null,
             modifyNode: {},//正在编辑的节点
@@ -151,6 +154,11 @@ export default {
                     this.$router.replace({path:'/editframe',query:{frameId:this.frameId}})
                 })
             }
+            //重新加载一次画布
+            this.$refs.container.dispose()
+            this.$nextTick(()=>{
+                this.$refs.container.init()
+            })
         },
         getMyList(){
             mychartInterface.classifyList().then((res)=>{
@@ -163,7 +171,7 @@ export default {
             if(this.frameId){
                 const res = await chartFrameInterface.getFrameDetail({ChartFrameworkId:Number(this.frameId)})
                 if(res.Ret!==200) return 
-                this.frameDetail = res.Data||{FrameworkName:'',FrameworkContent:''}
+                this.frameDetail = res.Data||{FrameworkName:'',FrameworkContent:'',Nodes:[]}
             }
             //获取到框架内容后再加载graph
             this.$nextTick(()=>{
@@ -171,6 +179,10 @@ export default {
             })
             
         },
+        //返回至列表页,并选中当前页的框架(如果是编辑)
+        returnListPage(){
+            this.$router.push({path:'/chartframe',query:{frameId:this.frameId||''}})
+        }
     },
     mounted(){
         this.getMyList()

+ 16 - 8
src/views/chartFrame_manage/index.vue

@@ -34,8 +34,8 @@
             </div>
             <div class="catalog-list">
                 <div class="public-catalog">
-                    <p>公共框架</p>
-                    <div class="catalog-tree">
+                    <p @click="expandPublic = !expandPublic" class="title">公共框架<span><i :class="{'el-icon-arrow-down':!expandPublic,'el-icon-arrow-up':expandPublic}"></i></span></p>
+                    <div class="catalog-tree" v-show="expandPublic">
                         <el-tree
                             ref="catalogTree"
                             class="catalog-tree other-tree"
@@ -167,7 +167,8 @@
                     <div class="frame-wrap">
                         <!--沙盘图组件-->
                         <FrameContainer ref="container"
-                            :FrameworkContent="currentFrame.FrameworkContent"
+                            :FrameworkContent="currentFrameDetail.FrameworkContent"
+                            :Nodes="currentFrameDetail.Nodes"
                             @showDialog="handleShowDialog"/>
                     </div>
                 </div>
@@ -243,7 +244,7 @@
 
 <script>
 import draggable from 'vuedraggable';
-import FrameContainer from './components/frameContainer.vue';
+import FrameContainer from './components/frameContainer';
 import chartDetail from '@/views/mychart_manage/components/chartDetailDia.vue';
 import { mychartInterface,chartFrameInterface } from '@/api/api.js';
 import {copyBlob} from '@/utils/svgToblob.js';
@@ -273,6 +274,7 @@ export default {
     data() {
         return {
             isSlideLeft:false,//控制左侧目录栏是否显示
+            expandPublic:false,//控制公共框架展开收起
 
             /* drag 我的框架相关 */
             dragStartIndex:0,
@@ -291,6 +293,7 @@ export default {
             currentList:[],//选择公共框架时,框架列表
             myFrameList:[],//我的框架列表
             currentFrame:{},//选择的框架
+            currentFrameDetail:{},//选中的框架详情
             /* frame node */
             myETADetailDialogShow:false,//点击节点时,弹出myeta图表详情弹框
             modifyFrame:{},//正在修改的框架
@@ -391,16 +394,21 @@ export default {
                 this.currentFrame={}
             }
         },
-        handleInitGraph(){
+        async handleInitGraph(){
+            //获取框架详情
+            if(this.currentFrame.ChartFrameworkId){
+                const res = await chartFrameInterface.getFrameDetail({ChartFrameworkId:Number(this.currentFrame.ChartFrameworkId)})
+                this.currentFrameDetail = res.Data||{}
+            }
             //判断一下框架内容是否是合法的JSON,否则置为空
             try{
-                JSON.parse(this.currentFrame.FrameworkContent)
+                JSON.parse(this.currentFrameDetail.FrameworkContent)
             }catch(e){
-                this.currentFrame.FrameworkContent = ''
+                this.currentFrameDetail.FrameworkContent = ''
             }
             this.$nextTick(()=>{
                 //若框架有内容,才加载画布
-                this.currentFrame.FrameworkContent&&this.$refs.container.init()
+                this.currentFrameDetail.FrameworkContent&&this.$refs.container.init()
             })
         },
         /* 拖动相关 */

+ 24 - 0
src/views/chartRelevance_manage/components/explainText.js

@@ -35,4 +35,28 @@ export const statisticFeatureListTextArr = [
     <p>2、根据频段数划分多个间距相同的区间(左闭右开,最后一个区间为左闭右闭),统计数据值落在每个区间的数据个数;</p>
     <p>3、频率=落在某区间数据个数/所选时间段内数据总个数;</p>
     <p>4、累计频率为从最小值所在区间对应的频率开始累加;</p>`
+]
+
+//跨品种分析
+export const crossVarietyAnalisisTextArr = [
+    `<p style="font-weight:bold;">一、处理逻辑</p>
+    <p>1、百分位计算公式:对所选时间范围内的数据,取最大值Max,最小值Min,计算Max-Min,百分位=(现值-Min)/(Max-Min),Max=Min时不计算</p>
+    <p>2、取值方式:</p>
+    <p>(1)通过“选择日期”和“时间长度”,时间范围为“选择日期”至选择的日期往过去推“时间长度”,在这个时间段找出指标最大值和最小值,进行百分位计算。</p>
+    <p>(2)若在(1)的时间范围下找不到值,则继续往过去找,直到找到值。</p>
+    <p>3、图表说明:</p>
+    <p> (1)某个品种会有多个坐标点(坐标点的数量取决于设置的日期数量);</p>
+    <p> (2)坐标点的X、Y值取实际日期与值。</p>`,
+
+    `<p style="font-weight:bold;">二、相关配置</p>
+    <p>1、基本配置</p>
+    <p>(1)管理员进行品种管理、标签管理;</p>
+    <p>(2)使用者对标签的品种关联指标。</p>
+    <p>2、计算配置</p>
+    <p>(1)时间长度:默认显示6个月;</p>
+    <p>(2)选择日期:最多支持选择5个日期;</p>
+    <p>(3)X轴坐标:选择标签;</p>
+    <p>(4)Y轴坐标:选择标签;</p>
+    <p>(5)图表名称:X轴坐标和Y轴坐标的拼接。</p>`
+
 ]

+ 670 - 0
src/views/chartRelevance_manage/crossVarietyAnalysis/chartEditor.vue

@@ -0,0 +1,670 @@
+<template>
+  <div class="crossVariety-edit-page" id="box">
+    <span
+      class="slide-icon slide-right"
+      @click="isSlideLeft = !isSlideLeft"
+      v-show="isSlideLeft"
+    >
+      <i class="el-icon-d-arrow-right"></i>
+    </span>
+    <div class="left-cont" v-show="!isSlideLeft" id="left">
+      <div class="left-top">
+        <div>
+          <el-button type="primary" @click="saveChartHandle">保存</el-button>
+          <el-button type="primary" plain @click="$router.back()">取消</el-button>
+
+        </div>
+        <div style="color:#409EFF;font-size: 16px;cursor: pointer;" @click="showExplain = true">
+            <i class="el-icon-document" style="font-size:22px;"></i>
+            操作说明
+        </div>
+      </div>
+
+      <div class="left-min">
+        <section class="section-item">
+          <div class="top">
+            <label>选择品种</label>
+            <el-button
+              v-permission="permissionBtn.statisticPermission.crossVariety_variety_edit"
+              type="text" 
+              @click="setDialogOpen('variety')"
+            >品种管理</el-button>
+          </div>
+          <div class="list" v-if="varietyOptions.length">
+            <el-tag 
+              size="small" 
+              :class="['tag',{'act':checkedVarietys.includes(item.ChartVarietyId)}]"
+              v-for="item in varietyOptions"
+              :key="item.ChartVarietyId"
+              @click="checkVarietyHandle(item)"
+            >{{item.ChartVarietyName}}</el-tag>
+          </div>
+
+          <tableNoData v-else text="暂无品种" size="mini"/>
+        </section>
+
+        <el-divider/>
+
+        <section class="section-item">
+          <div class="top">
+            <label>标签配置</label>
+            <el-button
+              v-permission="permissionBtn.statisticPermission.crossVariety_tag_edit"
+              type="text" 
+              @click="setDialogOpen('tag')"
+            >标签管理</el-button>
+          </div>
+          <div class="list" v-if="tagOptions.length">
+            <el-tag 
+              size="small" 
+              :class="item.VarietyTotal?'act tag':'tag'"
+              v-for="item in tagOptions"
+              :key="item.ChartTagId"
+              @click="setTagRelationOpen(item)"
+            >{{item.ChartTagName}}</el-tag>
+          </div>
+
+          <tableNoData text="暂无标签" size="mini" v-else/>
+        </section>
+
+      </div>
+      
+      <span
+        class="move-btn resize"
+        v-drag
+        id="resize"
+      ></span>
+      <span class="slide-icon slide-left" @click="isSlideLeft = !isSlideLeft">
+        <i class="el-icon-d-arrow-left"></i>
+      </span>
+    </div>
+    <div
+      class="right-cont"
+      id="right"
+      :style="isSlideLeft ? 'width:100%' : `width:80%`"
+    >
+      <div
+        class="chart-min-cont"
+      >
+        <div class="chart-wrapper" v-if="options.series&&options.series.length">
+          <div class="chart-show-cont"  v-if="!chartInfo.WarnMsg">
+            <div style="position:relative;">
+              <h2 class="chart-title">{{ chartInfo.ChartName }}</h2>
+              <Chart 
+                :options="options"
+                minHeight="400px" 
+                height="450px"
+                ref="chartRef"
+              />
+              <span class="chart-author">作者:{{ chartInfo.SysUserRealName || accountName}}</span>
+
+              <!-- 上下限 -->
+              <template>	
+                <div class="range-cont left">
+                  <el-input
+                    style="width: 60px; display: block"
+                    size="mini"
+                    type="number"
+                    :min="0"
+                    :max="100"
+                    placeholder="上限"
+                    v-model="chartLimit.max"
+                    @change="changeLimit"
+                  />
+                  <el-input
+                    class="min-data-input"
+                    size="mini"
+                    type="number"
+                    :min="0"
+                    :max="100"
+                    placeholder="下限"
+                    v-model="chartLimit.min"
+                    @change="changeLimit"
+                  />
+                </div>
+                <!-- x轴上下限 -->
+                <div class="range-cont bottom" > 
+                  <el-input
+                    class="left"
+                    size="mini"
+                    type="number"
+                    :min="0"
+                    :max="100"
+                    placeholder="下限"
+                    v-model="chartLimit.x_min"
+                    @change="changeLimit"
+                  />
+                  <el-input
+                    class="left"
+                    size="mini"
+                    type="number"
+                    :min="0"
+                    :max="100"
+                    placeholder="上限"
+                    v-model="chartLimit.x_max"
+                    @change="changeLimit"
+                  />
+                </div>
+              </template>
+              
+            </div>
+          </div>
+
+          <!-- 异常显示 -->
+          <p class="error-tip" style="min-height: 400px;" v-if="chartInfo.WarnMsg">{{chartInfo.WarnMsg}}</p>
+        </div>
+
+        <div class="nodata" v-else>
+          <tableNoData text="暂无信息"/>
+        </div>
+
+        <div class="bottom-wrapper">
+          <!-- 图表相关配置 -->
+          <chartFormSection
+            :tagList="tagOptions"
+            :chartInfo="chartInfo"
+            ref="chartFormRef"
+            @getData="getChartPreviewData"
+          />
+
+          <!-- 指标列表 -->
+          <edbTableSection :tableData="tableData"/>
+        </div>
+
+      </div> 
+
+    </div>
+
+    <!-- 管理弹窗 -->
+    <tagSetDialog
+      :isShow.sync="isShowSetDia"
+      :type="dialogType"
+      :dataList="dialogType==='variety'?varietyOptions:tagOptions"
+      @refresh="dialogType==='tag'?getTagList():getVarietyList();"
+    />
+
+    <!-- 标签指标配置 -->
+    <tagRelationDialog
+      :isShow.sync="isShowTagRelationDia"
+      :varietyList="varietyOptions"
+      :info="tagRelationInfo"
+      @refresh="getTagList"
+    />
+
+    <!-- 操作说明 -->
+    <ExplainDialog 
+        textArrName="crossVarietyAnalisisTextArr"
+        :show-explain="showExplain"
+        @close="showExplain = false"
+    />
+  </div>
+</template>
+
+<script>
+import { crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
+import { dataBaseInterface } from '@/api/api';
+import { chartSetMixin } from '@/views/dataEntry_manage/mixins/chartPublic';
+import Chart from '@/views/dataEntry_manage/components/chart';
+import ExplainDialog from '../components/explainDialog.vue';
+import chartFormSection from './components/chartFormSection.vue';
+import tagSetDialog from './components/tagSetDialog.vue';
+import tagRelationDialog from './components/tagRelationDialog.vue';
+import edbTableSection from './components/edbTableSection.vue'
+export default {
+  components: {
+    ExplainDialog,
+    Chart,
+    chartFormSection,
+    tagSetDialog,
+    tagRelationDialog,
+    edbTableSection
+  },
+  directives: {
+    drag(el, bindings) {
+      el.onmousedown = function (e) {
+        var init = e.clientX;
+        // console.log(init);
+        var box = $('#box')[0];
+        // console.log(box.clientWidth)
+        let total_wid = box.offsetWidth;
+        var left = $('#left')[0];
+        var right = $('#right')[0];
+        var initWidth = left.offsetWidth;
+        document.onmousemove = function (e) {
+          var end = e.clientX;
+          var newWidth = end - init + initWidth;
+          left.style.width = newWidth + 'px';
+          right.style.width = newWidth > 300 ? total_wid - newWidth + 'px' : total_wid - 320 + 'px';
+        };
+        document.onmouseup = function () {
+          document.onmousemove = document.onmouseup = null;
+          e.releaseCapture && e.releaseCapture();
+        };
+        e.setCapture && e.setCapture();
+        return false;
+      };
+    },
+  },
+  mixins: [ chartSetMixin ],
+  computed: {
+    accountName() {
+      return localStorage.getItem('userName')
+    }
+  },
+  data() {
+    return {
+      chart_code: this.$route.query.code || '',
+      isSlideLeft: false,
+      chartInfo: {
+        Source: 10,
+        ChartName: '',
+        SysUserRealName: ''
+      },
+
+      showExplain: false,
+
+      checkedVarietys: [], //选中的品种
+      varietyOptions: [],
+      tagOptions: [],
+
+      isShowSetDia: false,//管理弹窗
+      dialogType: '',
+
+      isShowTagRelationDia: false,//标签指标管理弹窗
+      tagRelationInfo: {
+        tagName:'',
+        id: 0
+      }
+    };
+  },
+  methods: {
+
+    /* 获取图表详情 */
+    async getChartDetail() {
+      if(!this.chart_code) return 
+
+      let res = await dataBaseInterface.getChartByCode({
+				UniqueCode: this.chart_code
+			})
+
+      if(res.Ret !== 200) return
+
+      const { ChartInfo,DataResp,EdbInfoList } = res.Data;
+      let configSet = JSON.parse(ChartInfo.ExtraConfig);
+      this.checkedVarietys = configSet.VarietyList;
+      this.$refs.chartFormRef.initData(configSet);
+
+      this.chartInfo = ChartInfo;
+      this.tableData = EdbInfoList;
+      this.crossVarietyChartData = DataResp;
+      this.chartLimit = {
+        min: DataResp.YMinValue,
+        max: DataResp.YMaxValue ,
+        x_min: DataResp.XMinValue,
+        x_max: DataResp.XMaxValue,
+      }
+
+      this.setCrossVarietyChart()
+    },
+
+    checkParmas(type='') {
+      if(!this.checkedVarietys.length || !this.$refs.chartFormRef.form.CalculateValue || !this.$refs.chartFormRef.form.TagX ||!this.$refs.chartFormRef.form.TagY){
+        
+        type && this.dealWarnings()
+        return false
+      }
+      return true
+    },
+
+    dealWarnings() {
+      const {CalculateValue,TagX,TagY } = this.$refs.chartFormRef.form;
+      let message = '';
+      
+      if(!this.checkedVarietys.length) message = '请选择品种'
+      else if(!CalculateValue) message = '请输入时间长度'
+      else if(!TagX) message = '请选择X轴坐标'
+      else if(!TagY) message = '请选择Y轴坐标'
+
+      this.$message.warning(message)
+    },
+
+    /* 获取标签列表 */
+    async getTagList() {
+      let res = await crossVarietyInterface.getTagList()
+
+      if(res.Ret !== 200) return
+      this.tagOptions = res.Data.List || []
+    },
+
+    async getVarietyList() {
+      let res = await crossVarietyInterface.getVarietyList()
+
+      if(res.Ret !== 200) return
+      this.varietyOptions = res.Data.List || []
+    },
+
+    /* 预览数据 */
+    async getChartPreviewData() {
+      if(!this.checkParmas()) return;
+
+      let params = {
+        VarietyList: this.checkedVarietys,
+        ...this.$refs.chartFormRef.form
+      }
+      let res = await crossVarietyInterface.chartPreview(params)
+
+      if(res.Ret !== 200) return
+
+      const { DataResp,EdbInfoList } = res.Data;
+      this.tableData = EdbInfoList;
+      
+      this.chartLimit = {
+        min: Number(DataResp.YMinValue),
+        max: Number(DataResp.YMaxValue),
+        x_min: Number(DataResp.XMinValue),
+        x_max: Number(DataResp.XMaxValue)
+      }
+      this.crossVarietyChartData = DataResp;
+      this.setCrossVarietyChart()
+    },
+    
+    /* 选择品种 */
+    checkVarietyHandle(item) {
+      this.checkedVarietys.includes(item.ChartVarietyId) 
+        ? this.checkedVarietys.splice(this.checkedVarietys.indexOf(item.ChartVarietyId),1) 
+        : this.checkedVarietys.push(item.ChartVarietyId)
+
+      this.getChartPreviewData();
+    },
+
+    /* 打开标签关联弹窗 */
+    setTagRelationOpen(item) {
+      this.tagRelationInfo = {
+        tagName: item.ChartTagName,
+        id: item.ChartTagId
+      }
+      this.isShowTagRelationDia = true;
+    },
+
+    /* 标签管理 */
+    setDialogOpen(type) {
+      this.dialogType = type;
+      this.isShowSetDia = true;
+    },
+
+    /* 保存图表 */
+    async saveChartHandle() {
+      if(!this.checkParmas('warning')) return;
+
+      let { min,max,x_min,x_max } = this.chartLimit;
+      let params = {
+        VarietyList: this.checkedVarietys,
+        ...this.$refs.chartFormRef.form,
+        XMinValue: String(x_min),
+				XMaxValue: String(x_max),
+				YMinValue: String(min),
+				YMaxValue: String(max),
+      }
+      let res = this.chart_code 
+        ? await crossVarietyInterface.chartEdit({...params,ChartInfoId: this.chartInfo.ChartInfoId})
+        : await crossVarietyInterface.chartAdd(params)
+
+      if(res.Ret !== 200) return
+      this.setChartImage(res.Data);
+    },
+
+     /* 设置图表封面图片 */
+    setChartImage(data) {
+      let svg = this.$refs.chartRef.chart.getSVG({
+        chart: {
+          width: 340,
+          height: 230,
+        }
+      });
+      let form = new FormData();
+      form.append('Img', svg);
+      this.setImageHandle(form,data);
+    },
+    async setImageHandle(form,{ UniqueCode,ChartInfoId }) {
+      let { Data } = await dataBaseInterface.uploadImgSvg(form);
+      await dataBaseInterface.setChartImage({
+        ChartInfoId: ChartInfoId,
+        ImageUrl: Data.ResourceUrl,
+      });
+
+			this.$message.success('保存成功');
+			this.$router.replace({
+				path: '/crossVarietyChartList',
+				query: {
+					code: UniqueCode,
+					id: ChartInfoId
+				}
+			})
+
+    },
+   
+    reloadRightWid() {
+      let total_wid = $('#box')[0].offsetWidth;
+      let left = $('#left')[0].offsetWidth;
+      let rigtWid = total_wid - left - 20 + 'px';
+      $('#right')[0].style.width = rigtWid;
+    },
+
+  },
+  mounted() {
+    this.getVarietyList();
+    this.getTagList();
+    this.getChartDetail();
+    window.addEventListener('resize', this.reloadRightWid);
+  },
+  destroyed() {
+    window.removeEventListener('resize', this.reloadRightWid);
+  },
+  beforeRouteEnter(to, from, next) {
+    if(to.query.code){
+      to.matched[1].name='编辑图表'
+    }else{
+      to.matched[1].name='添加图表'
+    }
+    next()
+  }
+};
+</script>
+<style lang="scss" scoped>
+.crossVariety-edit-page {
+  $font-small: 12px;
+  $font-normal: 14px;
+  display: flex;
+  * {
+    box-sizing: border-box;
+  }
+
+  .slide-icon {
+    padding: 20px 0;
+    /* display: block; */
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.3);
+    border-radius: 5px;
+    cursor: pointer;
+    position: absolute;
+    top: 50%;
+    transform: translateY(-50%);
+    z-index: 99;
+    &:hover {
+      background-color: rgba(0, 0, 0, 0.05);
+    }
+    &.slide-left {
+      right: 0;
+    }
+    &.slide-right {
+      left: 0;
+    }
+  }
+  .left-cont {
+    width: 380px;
+    min-width: 380px;
+    background: #fff;
+    margin-right: 20px;
+    border: 1px solid #ececec;
+    border-radius: 4px;
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.05);
+    height: calc(100vh - 113px);
+    overflow: hidden;
+    position: relative;
+    box-sizing: border-box;
+    .left-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;
+      justify-content: space-between;
+    }
+    .left-min {
+      padding: 0 20px;
+      max-height: calc(100vh - 190px);
+      overflow-y: auto;
+      
+      .section-item{
+        padding: 20px 0;
+        .top {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 15px;
+
+        }
+        .list {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 10px;
+          .tag {
+            color: #333;
+            background: #fff;
+            border-color: transparent;
+            cursor: pointer;
+            &.act {
+              color: #0052D9;
+              background-color: #e6eefb;
+              border-color: #ccdcf7;
+            }
+          }
+        }
+      }
+
+      .left-bottom {
+        margin-top: 20px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+     
+    }
+    .move-btn {
+      height: 100%;
+      width: 4px;
+      position: absolute;
+      right: 0px;
+      top: 0;
+      &:hover {
+        cursor: col-resize;
+      }
+    }
+  }
+  .right-cont {
+    width: 80%;
+    overflow-x: auto;
+    .chart-min-cont {
+      background: #fff;
+      min-width: 880px;
+      height: calc(100vh - 118px);
+      overflow-y: auto;
+      display: flex;
+      flex-wrap: wrap;
+      .chart-wrapper {
+				padding: 30px 0;
+        width: 100%;
+				/* =================== */
+				.chart-show-cont {
+					min-height: 400px;
+					padding: 0 100px 50px;
+					position: relative;
+					.chart-title {
+						font-size: 16px;
+						font-weight: normal;
+						text-align: center;
+						margin-bottom: 10px;
+					}
+					.chart-author {
+						font-size: 14px;
+						color: #333;
+						position: absolute;
+						bottom: -50px;
+						right: -70px;
+					}
+          .range-cont {
+            position: absolute;
+            top: 15%;
+            .min-data-input {
+              width: 60px;
+              display: block;
+            }
+            &.left {
+              left: -70px;
+            }
+            &.bottom {
+              width: 100%;
+              display: flex;
+              justify-content: space-between;
+              top: auto;
+              right: 0;
+              bottom: -4%;
+              .left {
+                width: 60px;
+                display: block;
+                flex-shrink: 0;
+              }
+            }
+          }
+				}
+				.options-cont {
+					display: flex;
+					flex-wrap: wrap;
+					justify-content: space-between;
+				}
+			}
+
+      @media screen and (min-width: 1711px) {
+        .min-data-input {
+          margin-top: 290px;
+        }
+      }
+      @media screen and (max-width: 1710px) {
+        .min-data-input {
+          margin-top: 250px;
+        }
+      }
+
+    }
+    .nodata {
+      background-color: #fff;
+      text-align: center;
+      font-size: 16px;
+      color: #666;
+      width: 100%;
+      padding: 100px 0;
+    }
+    .bottom-wrapper {
+      width: 100%;
+      padding: 20px;
+      border-top: 1px solid #ebeff6;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.crossVariety-edit-page {
+  .el-input--mini .el-input__inner {
+		padding: 0 0 0 4px;
+	}
+}
+</style>

+ 208 - 0
src/views/chartRelevance_manage/crossVarietyAnalysis/components/chartFormSection.vue

@@ -0,0 +1,208 @@
+<template>
+  <div class="form-cont">
+    <el-form
+      ref="diaForm"
+      label-position="left"
+      label-width="80px"
+      :model="form"
+      :rules="formRules"
+    >
+      <el-form-item label="时间长度" prop="CalculateValue">
+        <el-input
+          v-model="form.CalculateValue"
+          style="width: 140px"
+          :step="1"
+          type="number"
+          @change="(val) => {form.CalculateValue = Number(val);changeParams()}"
+        />
+        <el-select
+          v-model="form.CalculateUnit"
+          placeholder="请选择"
+          style="width: 100px"
+          @change="changeParams"
+        >
+          <el-option
+            v-for="item in unitOpt"
+            :key="item.val"
+            :label="item.label"
+            :value="item.val"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="X轴坐标" prop="TagX">
+        <el-select
+          v-model="form.TagX"
+          style="width: 240px"
+          placeholder="请选择"
+          @change="changeSeries"
+        >
+          <el-option
+            v-for="item in tagList"
+            :key="item.ChartTagId"
+            :label="item.ChartTagName"
+            :value="item.ChartTagId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="Y轴坐标" prop="TagY">
+        <el-select
+          v-model="form.TagY"
+          placeholder="请选择"
+          style="width: 240px"
+          @change="changeSeries"
+        >
+          <el-option
+            v-for="item in tagList"
+            :key="item.ChartTagId"
+            :label="item.ChartTagName"
+            :value="item.ChartTagId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="图表名称" prop="ChartName">
+        <el-input
+          v-model="form.ChartName"
+          style="width: 240px"
+          placeholder="请输入图表名称"
+          @change="changeParams"
+        />
+      </el-form-item>
+    </el-form>
+
+    <div class="date-section">
+      <label class="el-form-item__label">选择日期</label>
+      <ul>
+        <li class="date-li" v-for="(item,index) in form.DateConfigList" :key="index">
+          <el-select
+            v-model="item.DateType"
+            style="width: 150px"
+            placeholder="请选择"
+            @change="changeParams"
+          >
+            <el-option
+              v-for="item in dateOptions"
+              :key="item.val"
+              :label="item.label"
+              :value="item.val"
+            />
+          </el-select>
+
+          <el-input
+            v-model="item.Num"
+            v-if="item.DateType===2"
+            style="width: 140px"
+            :step="1"
+            type="number"
+            @change="(val) => {item.Num = Number(val);changeParams()}"
+          />
+
+          <el-button type="text" v-if="index===0" @click="addDateHandle">
+            <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 -2 16 16" fill="none">
+              <path d="M5.23966 8.48046H7.51966V10.7605C7.51966 10.8265 7.57366 10.8805 7.63966 10.8805H8.35966C8.42566 10.8805 8.47966 10.8265 8.47966 10.7605V8.48046H10.7597C10.8257 8.48046 10.8797 8.42646 10.8797 8.36046V7.64046C10.8797 7.57446 10.8257 7.52046 10.7597 7.52046H8.47966V5.24046C8.47966 5.17446 8.42566 5.12046 8.35966 5.12046H7.63966C7.57366 5.12046 7.51966 5.17446 7.51966 5.24046V7.52046H5.23966C5.17366 7.52046 5.11966 7.57446 5.11966 7.64046V8.36046C5.11966 8.42646 5.17366 8.48046 5.23966 8.48046Z" fill="#0052D9"/>
+              <path d="M13.52 2H2.48C2.2145 2 2 2.2145 2 2.48V13.52C2 13.7855 2.2145 14 2.48 14H13.52C13.7855 14 14 13.7855 14 13.52V2.48C14 2.2145 13.7855 2 13.52 2ZM12.92 12.92H3.08V3.08H12.92V12.92Z" fill="#0052D9"/>
+            </svg>添加日期
+          </el-button>
+
+          <i 
+            v-else 
+            class="el-icon-delete" 
+            style="cursor:pointer;color:#f00;"
+            @click="() => {form.DateConfigList.splice(index,1);changeParams()}"
+          />
+
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    tagList: {
+      type: Array,
+    },
+    chartInfo: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      form: {
+        ChartName:'',
+        Source: 10,
+        CalculateValue: 6,
+        CalculateUnit: '月',
+        TagX: '',
+        TagY: '',
+        DateConfigList: [
+          { DateType: 1,Num: 0 },
+        ],
+      },
+      unitOpt: [
+        { label: "年", val: "年" },
+        { label: "季", val: "季" },
+        { label: "月", val: "月" },
+        { label: "周", val: "周" },
+        { label: "天", val: "天" },
+      ],
+
+      dateOptions: [
+        { label: '最新日期',val: 1 },
+        { label: 'N天前',val: 2 },
+      ]
+    };
+  },
+  methods: {
+    addDateHandle() {
+      if(this.form.DateConfigList.length === 5) return this.$message.warning('日期数量已达上限')
+
+      this.form.DateConfigList.push({
+        DateType: 1,Num: 1
+      })
+      
+      this.changeParams()
+    },
+
+    initData(data) {
+      const { ChartName,CalculateValue,CalculateUnit,TagX,TagY,DateConfigList } = data;
+      this.form = {
+        ChartName,
+        CalculateValue,
+        CalculateUnit,
+        TagX,
+        TagY,
+        DateConfigList
+      }
+    },
+
+    changeSeries() {
+      let xName = this.tagList.find(_ => _.ChartTagId===Number(this.form.TagX))?this.tagList.find(_ => _.ChartTagId===Number(this.form.TagX)).ChartTagName:'';
+      let yName = this.tagList.find(_ => _.ChartTagId===Number(this.form.TagY))?this.tagList.find(_ => _.ChartTagId===Number(this.form.TagY)).ChartTagName:'';
+
+      this.form.ChartName = (xName&&yName) ? `${xName}百分位和${yName}百分位` : '';
+      this.changeParams()
+    },
+
+    changeParams() {
+      this.$emit('getData')
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.form-cont {
+  width: 100%;
+  display: flex;
+  gap: 50px;
+
+  .date-section {
+    display: flex;
+    .date-li {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+      margin-bottom: 20px;
+    }
+  }
+}
+</style>

+ 140 - 0
src/views/chartRelevance_manage/crossVarietyAnalysis/components/edbTableSection.vue

@@ -0,0 +1,140 @@
+<template>
+  <el-table
+    :data="tableData"
+    ref="tableRef"
+    highlight-current-row
+    border
+    >
+      <el-table-column
+        v-for="item in tableColums"
+        :key="item.label"
+        :label="item.label"
+        :width="item.widthsty"
+        :min-width="item.minwidthsty"
+        align="center"
+      >
+        <template slot-scope="scope">
+          <span>{{ scope.row[item.key] }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" key="Copy" align="center" width="140">
+        <template slot-scope="scope">
+          <span class="editsty" @click="copyCode(scope.row)">
+          <i class="el-icon-document-copy" />&nbsp;复制数据</span
+        ><br />
+        <span class="editsty" @click="viewTarget(scope.row)">查看数据</span>
+      </template>
+    </el-table-column>
+    <div slot="empty">
+      <tableNoData text="暂无指标" size="mini"/>
+    </div>
+  </el-table>
+</template>
+<script>
+import { dataBaseInterface } from '@/api/api';
+import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js';
+export default {
+  props: {
+    tableData: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      tableColums: [
+        {
+          label: '指标名称',
+          key: 'EdbName',
+          enKey:'EdbNameEn',
+					inputTip:'点击输入英文指标名称',
+          minwidthsty: '150px',
+        },
+        {
+          label: '指标ID',
+          key: 'EdbCode',
+          widthsty: '120px',
+        },
+        {
+          label: '更新频度',
+          key: 'Frequency',
+          enKey:'FrequencyEn',
+          minwidthsty: '60px',
+        },
+        {
+          label: '单位',
+          key: 'Unit',
+          enKey:'UnitEn',
+					inputTip:'英文单位',
+          minwidthsty: '50px',
+        },
+        {
+          label: '起始时间',
+          key: 'StartDate',
+          minwidthsty: '100px',
+        },
+        {
+          label: '最新日期',
+          key: 'LatestDate',
+          minwidthsty: '90px',
+        },
+        {
+          label: '最新值',
+          key: 'LatestValue',
+          minwidthsty: '90px',
+        },
+        {
+          label: '最近更新',
+          key: 'ModifyTime',
+          minwidthsty: '100px',
+        },
+        {
+          label: '数据来源',
+          key: 'SourceName',
+        },
+      ],//表格列
+    }
+  },
+  methods:{
+    /* 用于复制指标 */
+    async copyCode({EdbInfoId, EdbInfoCategoryType}) {
+      let params = {
+        PageSize: 100000,
+        CurrentIndex: 1,
+        EdbInfoId: EdbInfoId,
+      }
+      const res = EdbInfoCategoryType === 1 
+        ? await preDictEdbInterface.edbDataInfo(params)
+        : await dataBaseInterface.targetList(params);
+      if (res.Ret !== 200) return
+
+      let arr = res.Data.Item.DataList || [];
+
+      let str = '日期\t 值\n';
+      arr.forEach((item) => (str += `${item.DataTime}\t${item.Value}\n`));
+      this.$copyText(str).then(
+        (res) => {
+          this.$message.success('已成功复制!');
+        },
+        (err) => {
+          this.$message.error('复制失败!');
+        }
+      );
+    },
+
+    /* 查看数据 跳转指标库展开具体指标 */
+    viewTarget({ UniqueCode,EdbInfoId,EdbInfoCategoryType,ClassifyId }) {
+      let path = EdbInfoCategoryType ? '/predictEdb' : '/database';
+      let {href} = this.$router.resolve({path, query: {
+        code: UniqueCode,
+        id: EdbInfoId,
+        classifyId:ClassifyId
+      }});
+			window.open(href,'_blank');
+    },
+  },
+}
+</script>
+<style scoped lang='scss'>
+
+</style>

+ 182 - 0
src/views/chartRelevance_manage/crossVarietyAnalysis/components/tagRelationDialog.vue

@@ -0,0 +1,182 @@
+<template>
+  <el-dialog
+    :visible.sync="isShow"
+    :close-on-click-modal="false"
+    :modal-append-to-body="false"
+    :title="`${info.tagName}指标配置`"
+    @close="cancelHandle"
+    custom-class="tag-relation-dialog"
+    center
+    width="650px"
+    v-dialogDrag
+  >
+    <div class="dialog-min">
+      <ul class="list-ul">
+        <li class="list-li" v-for="(item,index) in list" :key="index">
+          <label class="variety-name">{{item.varietyName}}</label>
+
+          <selectTarget
+            :defaultId="item.edbInfoId"
+            :defaultOpt="searchOptions"
+            @select="e => selectTargetHandle(e,item)"  
+            :filter="false"
+            style="max-width: 200px;"
+          />
+          <el-date-picker
+            v-model="item.endDate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            disabled
+            style="width:140px;"
+            placeholder="最新日期"
+          />
+
+          <el-input
+            v-model="item.endValue"
+            style="width: 100px;"
+            disabled
+            placeholder="最新值"
+          />
+
+        </li>
+      </ul>
+
+      <div class="dialog-bot">
+        <el-button type="primary" style="margin-right: 20px" @click="saveHandle"
+          >保存</el-button
+        >
+        <el-button type="primary" plain @click="cancelHandle">取消</el-button>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+<script>
+import { crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
+import selectTarget from '@/views/chartRelevance_manage/components/selectTarget.vue';
+export default {
+  components: { selectTarget },
+  props: {
+    isShow: {
+      type: Boolean
+    },
+    info: {
+      type:  Object
+    },
+    varietyList: {
+      type: Array
+    }
+  },
+  watch: {
+    isShow(nval) {
+      if(!nval) return
+
+      this.getRelationList() 
+    }
+  },
+  data() {
+    return {
+      list: [],
+      searchOptions: []
+    }
+  },
+  methods:{
+    selectTargetHandle(e,item) {
+      if(!e){
+        item.edbName = '';
+        item.edbInfoId = '';
+        item.endDate = '';
+        item.endValue = '';
+        return
+      } 
+      const { EndDate,EndValue,EdbName,EdbInfoId } = e;
+      
+      item.edbName = EdbName;
+      item.edbInfoId = EdbInfoId;
+      item.endDate = EndDate;
+      item.endValue = EndValue;
+    },
+
+    /* 获取标签关联品种列表 */
+    async getRelationList() {
+      let res = await crossVarietyInterface.getTagRelation({ ChartTagId: this.info.id })
+      if(res.Ret!==200) return
+
+      let relationArr = res.Data.List || [];
+
+      this.list = this.varietyList.map(_ => {
+        let relationItem = relationArr.length ? relationArr.find(item => item.ChartVarietyId===_.ChartVarietyId) : undefined
+
+        return {
+          varietyName: _.ChartVarietyName,
+          varietyId: _.ChartVarietyId,
+          edbName: relationItem?relationItem.EdbName:'',
+          edbInfoId: relationItem?relationItem.EdbInfoId:0,
+          endDate: relationItem?relationItem.EndDate:'',
+          endValue: relationItem?relationItem.EndValue:''
+        }
+      })
+
+      this.searchOptions = this.list.map(_ => ({
+        EdbName: _.edbName,
+        EdbInfoId: _.edbInfoId
+      }))
+    },
+
+    /* 保存`` */
+    saveHandle: _.debounce(async function() {
+      let params = {
+        ChartTagId: this.info.id,
+        VarietyEdb: this.list.map(_ => ({
+          ChartVarietyId: _.varietyId,
+          EdbInfoId: _.edbInfoId
+        })).filter(_ => _.EdbInfoId)
+      }
+      let res = await crossVarietyInterface.setTagRelationVariety(params)
+
+      if(res.Ret!==200) return
+      this.$message.success('配置保存成功')
+      this.$emit('refresh')
+      this.cancelHandle()
+    },300),
+
+    cancelHandle() {
+      this.list = [];
+      this.searchOptions=[];
+      this.$emit('update:isShow',false)
+    }
+  },
+}
+</script>
+<style scoped lang='scss'>
+.dialog-min {
+  padding: 30px 0 50px;
+  .list-ul{
+    padding: 0 15px;
+    max-height: calc(100vh - 500px);
+    min-height: 300px;
+    overflow-y: auto;
+    .list-li {
+      display: flex;
+      align-items: center;
+      gap: 20px;
+      margin-bottom: 20px;
+      .variety-name {
+        /* margin-right: 20px; */
+        width: 100px;
+      }
+    }
+  }
+  .dialog-bot {
+    display: flex;
+    margin-top: 30px;
+    justify-content: center;
+  }
+}
+</style>
+<style lang="scss">
+  /* .tag-relation-dialog .el-dialog__body {
+    max-height: calc(100vh - 350px);
+    min-height: 300px;
+    overflow-y: auto;
+  } */
+</style>

+ 207 - 0
src/views/chartRelevance_manage/crossVarietyAnalysis/components/tagSetDialog.vue

@@ -0,0 +1,207 @@
+<template>
+  <el-dialog
+    :visible.sync="isShow"
+    :close-on-click-modal="false"
+    :modal-append-to-body="false"
+    :title="type==='tag'?'标签管理':'品种管理'"
+    @close="cancelHandle"
+    custom-class="dialog"
+    center
+    width="560px"
+    v-dialogDrag
+  >
+    <div class="dialog-min">
+      <el-input
+        v-model="searchTxt"
+        placeholder="请输入名称"
+        prefix-icon="el-icon-search"
+        size="medium"
+        clearable 
+        @input="inputVisible = false;"
+      />
+      <div class="tag-list">
+        <div
+          v-for="(item,index) in showList"
+          :key="item.id"
+          class="el-tag"
+          :style="item.isEdit&&'padding:0;border:none;'"
+          @dblclick="editTagOpen(item,index)"
+        >
+
+          <el-input
+            v-model="item.label"
+            v-if="item.isEdit"
+            class="input-edit"
+            :ref="`inputRef${index}`"
+            size="small"
+            @blur="editItemHandle(item)"
+          />
+          <span v-else>
+            {{ item.label }}
+            <el-popconfirm
+              :title="`是否确认删除该${type==='tag'?'标签':'品种'}`"
+              @onConfirm="closeItemHandle(item)"
+            >
+              <i
+                slot="reference"
+                class="el-tag__close el-icon-close" 
+              />
+                <!-- @click.stop="closeItemHandle(item)" -->
+            </el-popconfirm>
+          </span>
+        </div>
+
+        <el-input
+          class="input-edit"
+          v-if="inputVisible"
+          v-model="inputValue"
+          ref="saveTagInput"
+          size="small"
+          @blur="addNewTagHandle"
+        >
+        </el-input>
+        <el-button v-else size="small" @click="addNewItem">+ {{type==='tag'?'标签':'品种'}}</el-button>
+
+      </div>
+    </div>
+
+  </el-dialog>
+</template>
+<script>
+import { crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
+export default {
+  props: {
+    isShow: {
+      type: Boolean
+    },
+    type: { //tag  variety
+      type: String
+    },
+    dataList: {
+      type: Array,
+      default: () => []
+    }
+  },
+  watch: {
+    isShow(nval) {
+      if(!nval) return
+      this.initData()
+    },
+
+    dataList(nval) {
+      this.initData();
+    }
+  },
+  computed: {
+    showList() {
+      return this.list.length ? this.list.filter(_ => _.label.includes(this.searchTxt)) : []
+    }
+  },
+  data() {
+    return {
+      searchTxt: '',
+      inputVisible: false,
+      inputValue: '',
+      list: []
+    }
+  },
+  mounted(){
+
+  },
+  methods:{
+    /* 关闭标签 */
+    closeItemHandle(item) {
+      this.delItem(item)
+    },
+
+    async delItem(item) {
+      let res = this.type==='tag' 
+        ? await crossVarietyInterface.tagDel({ChartTagId:item.id})
+        : await crossVarietyInterface.varietyDel({ChartVarietyId:item.id})
+
+      if(res.Ret !== 200) return
+      this.$message.success('删除成功')
+      
+      this.refreshData()
+    },
+
+    addNewItem() {
+      this.inputValue = '';
+      this.inputVisible = true;
+      this.$nextTick(_ => {
+        this.$refs.saveTagInput.$refs.input.focus();
+      });
+    },
+
+    /* 新增标签 */
+    async addNewTagHandle() {
+      this.inputVisible = false;
+      if(!this.inputValue) return
+
+      let res = this.type==='tag' 
+        ? await crossVarietyInterface.tagAdd({ TagName: this.inputValue})
+        : await crossVarietyInterface.varietyAdd({ VarietyName: this.inputValue})
+        
+      if(res.Ret!==200) return
+      this.refreshData()
+      
+    },
+
+    editTagOpen(item,index) {
+      this.$set(item,'isEdit',true)
+      this.$nextTick(_ => {
+        this.$refs[`inputRef${index}`][0].$refs.input.focus();
+      });
+    },
+
+    /* 编辑标签 */
+    async editItemHandle(item) {
+      if(item.label===item.ChartVarietyName || item.label===item.ChartTagName) {
+        item.isEdit = false;
+        return
+      }
+
+      let res = this.type==='tag' 
+        ? await crossVarietyInterface.tagEdit({ TagName: item.label,ChartTagId:item.id})
+        : await crossVarietyInterface.varietyEdit({ VarietyName: item.label,ChartVarietyId:item.id})
+      if(res.Ret !== 200) return
+
+      item.isEdit = false;
+      this.refreshData()
+    },
+
+    refreshData() {
+      this.searchTxt = ''
+      this.$emit('refresh')
+    },
+
+    initData() {
+      this.list = this.dataList.map(_ => ({
+        label: _.ChartTagName || _.ChartVarietyName,
+        id:  _.ChartTagId || _.ChartVarietyId,
+        ..._
+      }))
+    },
+
+    cancelHandle() {
+      this.searchTxt = '';
+      this.inputVisible = false;
+      this.$emit('update:isShow',false)
+    }
+  },
+}
+</script>
+<style scoped lang='scss'>
+.dialog-min {
+  padding: 0 0 60px;
+  .tag-list { 
+    margin-top: 30px;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 15px;
+  }
+  .input-edit {
+    width: 100px;
+  }
+}
+</style>

+ 570 - 0
src/views/chartRelevance_manage/crossVarietyAnalysis/list.vue

@@ -0,0 +1,570 @@
+<template>
+	<div class=" pub-chart-box" v-if="showData">
+		<span
+			class="slide-icon slide-right"
+			@click="isSlideLeft = !isSlideLeft"
+			v-show="isSlideLeft"
+		>
+			<i class="el-icon-d-arrow-right"></i>
+		</span>
+
+		<div class="data-sheet-main" id="box">
+      <div class="main-left left" id="left" v-show="!isSlideLeft">
+				<div class="datasheet_top">
+						<el-button v-permission="permissionBtn.statisticPermission.crossVariety_addChart"
+							 type="primary" @click="goAddChart">添加图表</el-button>
+				</div>
+
+				<div class="search-cont">
+					<el-checkbox v-model="isShowMe"  
+						v-permission="permissionBtn.statisticPermission.crossVariety_onlyMine"
+						@change="() => { getTreeData();getPublicList() }">只看我的</el-checkbox>
+					<el-select
+						v-model="search_txt"
+						v-loadMore="searchLoad"
+						:filterable="!search_txt"
+						remote
+						clearable
+						placeholder="请输入图表名称"
+						style="width: 100%; margin-top: 20px"
+						:remote-method="searchHandle"
+						@click.native="inputFocusHandle"
+					>
+						<i slot="prefix" class="el-input__icon el-icon-search"></i>
+						<el-option
+							v-for="item in searchOptions"
+							:key="item.ChartInfoId"
+							:label="item.ChartName"
+							:value="item.ChartInfoId"
+						>
+						</el-option>
+					</el-select>
+				</div>
+				<div class="tree-cont">
+					<el-tree
+						ref="treeRef"
+						class="target_tree"
+						:data="treeData"
+						node-key="UniqueCode"
+						:props="defaultProp"
+						:allow-drag="canDragHandle"
+						:allow-drop="canDropHandle"
+						:current-node-key="select_node"
+						:default-expanded-keys="defaultShowNodes"
+						draggable
+						:expand-on-click-node="false"
+						check-strictly
+						empty-text="暂无分类"
+						@node-expand="handleNodeExpand"
+						@node-collapse="handleNodeCollapse"
+						@current-change="nodeChange"
+						@node-drop="dropOverHandle"
+						@node-drag-end="dropMouseLeave"
+						@node-drag-leave="dropMouseLeave"
+						@node-drag-enter="dropMouseOver"
+					>
+						<span class="custom-tree-node" slot-scope="{ node, data }">
+							<el-input
+								ref="editVal"
+								style="width: 90px"
+								placeholder="请输入值"
+								class="label-input"
+								v-model="new_label"
+								v-if="data.isEdit&&permissionBtn.isShowBtn('statisticPermission','crossVariety_classifyOpt_edit')"
+								@blur="changeValue(data)"
+							/>
+							<span
+								@dblclick.stop="handleDblClick(data)"
+								v-else
+								class="text_oneLine node_label"
+								:style="`width:${
+									(select_node === data.UniqueCode && node.Nodewidth) || ''
+								}`"
+							>
+								<span>{{ data.ChartClassifyName }}</span>
+							</span>
+							<span
+								style="display: flex; align-items: center"
+								v-if="select_node === data.UniqueCode"
+							>
+								<img
+									src="~@/assets/img/data_m/move_ico.png"
+									alt=""
+									style="width: 14px; height: 14px; margin-right: 8px"
+								/>
+								<img
+									src="~@/assets/img/set_m/edit.png"
+									alt=""
+									style="width: 15px; height: 14px; margin-right: 8px"
+									@click.stop="editNode(node, data)"
+									v-if="!data.ChartInfoId&&permissionBtn.isShowBtn('statisticPermission','crossVariety_classifyOpt_edit')"
+								/>
+								<!-- <img
+									slot="reference"
+									src="~@/assets/img/set_m/del.png"
+									alt=""
+									style="width: 14px; height: 14px"
+									@click.stop="removeNode(node, data)"
+									v-if="!data.ChartInfoId&&permissionBtn.isShowBtn('statisticPermission','crossVariety_classifyOpt_delete')"
+								/> -->
+							</span>
+						</span>
+					</el-tree>
+
+				</div>
+				<span
+					class="move-btn resize"
+					v-drag
+					id="resize"
+					@mousemove="dynamicNode && resetNodeStyle(dynamicNode)"
+				></span>
+				<span class="slide-icon slide-left" @click="isSlideLeft = !isSlideLeft">
+					<i class="el-icon-d-arrow-left"></i>
+				</span>
+      </div>
+
+			<div class="main-right" id="right" :style="isSlideLeft ? 'width:100%' : 'width:80%'">
+
+				<!-- 图表详情 -->
+				<div class="chart-detail-wrapper" v-if="chartInfo.ChartInfoId">
+					<el-row class="bottom-min">
+              <el-col
+                :span="21"
+                style="padding: 20px 0;"
+              >
+                <div class="chartEn-mark" v-show="chartInfo.IsEnChart" style="top: 0;left: 0;">En</div>
+                <div class="chart-show-cont"  v-if="!chartInfo.WarnMsg">
+                  <div class="chartWrapper" id="chartWrapper">
+                    <h2 class="chart-title">{{ chartInfo.ChartName }}</h2>
+                    <Chart 
+											:options="options"
+											minHeight="440px" 
+											height="500px"
+											ref="chartRef"
+										/>
+                  </div>
+                  <span class="chart-author">作者:{{ chartInfo.SysUserRealName}}</span>
+                </div>
+
+                <!-- 异常显示 -->
+                <p class="error-tip" style="min-height: 400px;" v-if="chartInfo.WarnMsg">{{chartInfo.WarnMsg}}</p>
+              </el-col>
+              <el-col :span="3" style="position: absolute;height: 100%;right: 0;">
+								<!-- 操作 -->
+								<chartHandlesWrap
+									:chartInfo="chartInfo"
+									:linkUrl="linkUrl"
+									@copyChartConfirm="copyChartConfirm"
+									@addMychartHandle="addMychartHandle"
+									@refreshHandle="refreshHandle"
+									@saveChartOtherHandle="saveChartOtherHandle"
+									@editChartHandle="editChartHandle"
+									@openEnNameDia="openEnNameDia"
+									@delChartHandle="delChartHandle"
+								/>
+              </el-col>
+          </el-row>
+
+					<!-- 指标列表 -->
+					<edbTableSection :tableData="tableData"/>
+
+				</div>
+				<!-- 列表 -->
+				<chartListWrap
+          v-else 
+          :total="chart_total"
+          :list="chartList" 
+          @loadMoreHandle="loadMoreHandle"
+          @detailShowHandle="detailShowHandle"
+          @addMychartHandle="addMychartHandle"
+          ref="chartListWrap"
+        />
+			</div>
+		</div>
+
+
+		<!-- 分类弹窗 -->
+		<classify-dia
+			:isOpenDialog.sync="classifyDia"
+			:title="dialog_title"
+			:form="classifyForm"
+			@successCallback="getTreeData"
+		/>
+
+		 <!-- 加入我的图库弹窗 -->
+    <addMyClassifyDia
+      :isAddMyDialog="isAddMyChart"
+      :add_id="add_chart_id"
+      :add_ids="add_ids"
+      @cancel="isAddMyChart = false"
+      @addSuccess="addMySuccess"
+    />
+
+		 <!-- 图表另存 -->
+    <SaveChartOther 
+      :show.sync="isShowSaveOther" 
+      fromType="chartsetting"
+			source="cross_variety"
+      :data="chartInfo"
+      @ensure="getTreeData"
+    />
+
+		<!-- 输入英文指标弹窗 -->
+		<set-en-name-dia 
+			:isOpenDialog="setEnName" 
+		  :formData="formItemArray"
+			@cancel="setEnName=false"
+   	  @updateEnName="updateEnName"
+		/>
+
+	</div>
+</template>
+
+<script>
+import { crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
+import leftMixin from '../mixins/classifyMixin';
+import Chart from '@/views/dataEntry_manage/components/chart';
+import classifyDia from '@/views/datasheet_manage/components/sheetClassifyDia.vue';
+import addMyClassifyDia from '@/views/dataEntry_manage/components/addMyClassifyDia';
+import SaveChartOther from '@/views/dataEntry_manage/components/SaveChartOther';
+import setEnNameDia from '@/views/dataEntry_manage/components/setEnNameDia.vue'
+import { chartSetMixin } from '@/views/dataEntry_manage/mixins/chartPublic';
+import { copyOtherOptions } from '@/utils/defaultOptions';
+import edbTableSection from './components/edbTableSection.vue';
+export default {
+	components: {
+		classifyDia,
+		Chart,
+		addMyClassifyDia,
+		SaveChartOther,
+		setEnNameDia,
+		edbTableSection
+	},
+	mixins:[ leftMixin,chartSetMixin ],
+	computed: {
+		/* 分享地址 */
+    linkUrl() {
+      const LINK_CHART_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/chartshow';
+      return `${LINK_CHART_URL}?code=${this.chartInfo.UniqueCode}&fromType=share?&lang=${this.currentLang}`
+    }
+	},
+	data () {
+		return {
+			showData: false,
+			refreshLoading: false,
+			isShowMe: false,
+			search_txt: '',
+			searchOptions:[],
+			isSlideLeft: false,//左侧分类收起
+
+			select_node: '',//节点唯一标识code
+			select_classify: '',
+			new_label:'',//双击修改的value
+			treeData: [], //分类数据
+      defaultShowNodes: [], //展开节点
+      defaultProp: {
+        label: 'ChartClassifyName',
+        children: 'Children',
+      }, //树结构配置项
+			dynamicNode: null,
+
+			/* 分类弹窗 */
+			dialog_title:'',
+			classifyDia: false, //
+			classifyForm: {},
+
+			select_id: '',//选中的图表id
+			chartInfo: {},
+			chart_title:'',//图表标题 双击标题修改时来存储最新值
+
+			isAddMyChart: false, //加入图库弹窗
+      add_chart_id: 0, //要加入的图表
+			add_ids:[],//加入时已有的分类
+
+			/* 图表列表 */
+			publicHaveMove: true,//是否还有列表数据
+			chartList: [],
+			chart_total: 0,
+			chart_page: 1,
+			chart_pages_size: 16,
+
+			search_page: 1,
+			search_have_more: false,
+			current_search:'',
+		};
+	},
+	methods: {
+
+		/* 添加图表 */
+		goAddChart() {
+			this.$router.push({ path: '/crossVarietyChartEditor' });
+		},
+
+		/* 获取分类 */
+		getTreeData(params=null) {
+			crossVarietyInterface.classifyList({IsShowMe:this.isShowMe}).then(res => {
+				const { Ret,Data } = res;
+				if(Ret !== 200) return
+
+				this.showData = true;
+				this.treeData = Data.AllNodes || [];
+				this.$nextTick(() => {
+					/* 新增完成后 处理树展开和选中 */
+					params && this.selectCurrentNode(params);
+				});
+
+			})
+		},
+
+		/* 搜索 */
+		searchHandle(query) {
+			this.search_page = 1;
+			this.current_search = query;
+			this.searchApi(this.current_search)
+		},
+
+		searchApi(query,page=1) {
+      /* 查找列表 */
+      crossVarietyInterface
+        .searchChart({
+          Keyword: query,
+          IsShowMe:this.isShowMe,
+          CurrentIndex: page
+        })
+        .then((res) => {
+          if (res.Ret !== 200) return
+          const { List,Paging } = res.Data;
+          this.search_have_more = page < Paging.Pages;
+          this.searchOptions = page === 1 ? List : [...this.searchOptions,...List];
+        });
+		},
+
+		/* 聚焦获取当前检索 */
+		inputFocusHandle(e) {
+			this.search_page = 1;
+			this.current_search = e.target.value;
+      if(this.current_search) {
+        this.searchApi(this.current_search)
+      }else {
+        this.searchOptions = [];
+      }
+		},
+
+		searchLoad() {
+			if(!this.search_have_more) return;
+			this.searchApi(this.current_search,++this.search_page);
+		},
+
+		/* 选中分类变化时 */
+		nodeChange({ UniqueCode,ChartInfoId,ChartClassifyId },node) {
+			this.search_txt = '';
+			this.select_node = UniqueCode;
+			this.select_classify = !ChartInfoId ? ChartClassifyId : 0;
+			this.select_id = ChartInfoId || 0;
+			this.resetNodeStyle(node);
+			this.dynamicNode = node;
+		},
+
+
+    /* 编辑节点 */
+    editNode(node, { ChartClassifyName,ChartClassifyId }) {
+
+      this.dialog_title = '编辑图表分类';
+      /* 编辑目录 */
+      this.classifyForm = {
+        classify_name: ChartClassifyName,
+        classify_id: ChartClassifyId,
+      };
+      this.classifyDia = true;
+    },
+
+		 /* 删除方法 */
+    delApi(ChartInfoId) {
+      crossVarietyInterface.chartDel({
+				ChartInfoId
+			})
+      .then((res) => {
+				if (res.Ret !== 200) return
+				this.$message.success(res.Msg);
+
+				if (!res.Data.ChartInfoId) this.select_id = '';
+
+				//删除自动显示下一张
+				res.Data.ChartInfoId
+					? this.getTreeData({
+							code: res.Data.UniqueCode,
+							id: res.Data.ChartInfoId
+						})
+					: this.getTreeData();
+			});
+    },
+
+		/* 分类成功回调 */
+		classifyCallback(type) {
+			this.getTreeData();
+
+			if (type === 'add') {
+        //新增分类完成之后,展开父节点显示刚新增的分类,若已展开节点则不做处理
+        let code = this.add_parent_id;
+        let flag = this.defaultShowNodes.some(item => item === code);
+        // console.log(flag)
+        !flag && this.defaultShowNodes.push(code);
+        this.add_parent_id = '';
+
+      }
+		},
+
+		/* 获取图表列表 */
+		getPublicList() {
+			crossVarietyInterface.getChartList({
+				CurrentIndex: this.chart_page,
+				PageSize: this.chart_pages_size,
+        ChartClassifyId: this.select_classify || 0,
+				IsShowMe: this.isShowMe
+			}).then(res => {
+				if(res.Ret !== 200) return
+
+				this.publicHaveMove = res.Data
+            ? this.chart_page < res.Data.Paging.Pages
+            : false;
+          this.chartList = res.Data
+            ? this.chart_page === 1
+              ? res.Data.List
+              : [...this.chartList, ...res.Data.List]
+            : [];
+        this.chart_total = res.Data ? res.Data.Paging.Totals : 0;
+			})
+		},
+
+		getChartInfo() {
+			this.getDetailHandle();
+		},
+
+		/* 获取图表详情 */
+		getDetailHandle() {
+			crossVarietyInterface.chartDetail({
+				ChartInfoId: this.select_id
+			}).then(res => {
+				if(res.Ret !== 200) return
+
+				const { ChartInfo,DataResp,EdbInfoList } = res.Data;
+
+				this.chartInfo = ChartInfo;
+				this.tableData = EdbInfoList;
+				this.crossVarietyChartData = DataResp;
+				this.chartLimit = {
+					min: DataResp.YMinValue,
+					max: DataResp.YMaxValue ,
+					x_min: DataResp.XMinValue,
+					x_max: DataResp.XMaxValue,
+				}
+
+				this.setCrossVarietyChart()
+			})
+		},
+
+		/* 删除图表 */
+		delChartHandle() {
+			const { ChartClassifyId,ChartInfoId } = this.chartInfo;
+			this.$confirm('删除后该图表将不能再引用,确认删除吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.delApi(ChartInfoId)
+        })
+        .catch(() => {});
+		},
+
+		/* 刷新图表 */
+		refreshHandle() {
+			this.refreshLoading = this.$loading({
+        lock: true,
+        target: '.main-right',
+        text: '刷新图表中...',
+        spinner: 'el-icon-loading',
+        background: 'rgba(255, 255, 255, 0.8)',
+      });
+      crossVarietyInterface.refreshChart({
+          ChartInfoId: this.chartInfo.ChartInfoId,
+      }).then((res) => {
+				this.refreshLoading.close();
+				if (res.Ret === 200) {
+					this.getDetailHandle();
+					this.$message.success(res.Msg);
+				}
+			});
+		},
+
+		/* 编辑图表 */
+		editChartHandle() {
+			this.$router.push({
+        path: '/crossVarietyChartEditor',
+        query: {
+          code: this.chartInfo.UniqueCode,
+					id: this.chartInfo.ChartInfoId
+        }
+      })
+		},
+
+		copyChartHandle:_.debounce(function(type){
+      let chartsName = this.currentLang=='ch'?this.chartInfo.ChartName:this.chartInfo.ChartNameEn
+      let { heightNum, widthNum , newTitle , dynamic_copyOptions} = this.dynamicWidthAndHeight(type,this.chartInfo.ChartType,chartsName,1)
+      const chartType = 'seasonLegend';
+      // 英文转SVG设置变动
+      if(this.currentLang == 'en'){
+        // 如果竖轴坐标单位为'英文单位',表示客户没填,转成svg时置为空
+        this.$refs.chartRef.chart.options.yAxis.forEach(it => {
+          if(it.title.text == '英文单位') it.title.text='' 
+        });
+      }
+      let svg = this.$refs.chartRef.chart.getSVG({
+        chart:{
+          width:widthNum,
+          height:heightNum,
+          backgroundColor:"rgba(255, 255, 255, 0)",
+        },
+        title: {
+          text: newTitle,
+          margin: 10,
+          style: {
+            fontSize: '18px'
+          }
+        },
+        legend:{
+          ...copyOtherOptions[chartType],
+          ...dynamic_copyOptions[chartType]
+          }
+      });
+      this.copyBlobItem(widthNum,heightNum,svg,type);
+    },500),
+    
+		handleDblClick(data){
+			if(!this.permissionBtn.isShowBtn('statisticPermission','crossVariety_classifyOpt_edit')) return 
+			this.editNodeLabel(data)
+		}
+	},
+	mounted() {
+
+		if(this.$route.query.code) {
+      this.getTreeData({code: this.$route.query.code,id: Number(this.$route.query.id)})
+    } else {
+      this.getTreeData();
+      this.getPublicList();
+    }
+
+		window.addEventListener('resize', this.reloadRightWid);
+	},
+	destroyed() {
+		window.removeEventListener('resize', this.reloadRightWid);
+	}
+}
+</script>
+<style lang='scss' scoped>
+@import '../css/index.scss';
+</style>
+
+<style lang="scss">
+@import '../css/pub.scss';
+</style>

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

@@ -136,7 +136,7 @@ $normal-font: 14px;
           margin-bottom: 20px;
 
           .chart-show-cont {
-            padding: 0 160px 0 120px;
+            padding: 0 160px 0 100px;
             position: relative;
             min-height: 400px;
             .chart-title {

+ 4 - 4
src/views/chartRelevance_manage/fittingEquationChartEditor.vue → src/views/chartRelevance_manage/fittingEquation/fittingEquationChartEditor.vue

@@ -174,10 +174,10 @@
 import { fittingEquationInterface } from '@/api/modules/chartRelevanceApi';
 import { yearSelector } from '@/utils/defaultOptions';
 import Chart from '@/views/dataEntry_manage/components/chart';
-import selectTarget from './components/selectTarget.vue';
-import fittingEquationSaveDia from './components/fittingEquationSaveDia.vue';
-import chartCard from './components/chartCard.vue';
-import ExplainDialog from './components/explainDialog.vue';
+import selectTarget from '../components/selectTarget.vue';
+import fittingEquationSaveDia from '../components/fittingEquationSaveDia.vue';
+import chartCard from '../components/chartCard.vue';
+import ExplainDialog from '../components/explainDialog.vue';
 export default {
   components: { Chart,selectTarget,fittingEquationSaveDia,chartCard,ExplainDialog },
 	directives: {

+ 3 - 3
src/views/chartRelevance_manage/fittingEquationList.vue → src/views/chartRelevance_manage/fittingEquation/fittingEquationList.vue

@@ -243,7 +243,7 @@
 
 <script>
 import { fittingEquationInterface } from '@/api/modules/chartRelevanceApi';
-import leftMixin from './mixins/classifyMixin';
+import leftMixin from '../mixins/classifyMixin';
 import Chart from '@/views/dataEntry_manage/components/chart';
 import changeLang from "@/views/dataEntry_manage/components/changeLang.vue"
 import classifyDia from '@/views/datasheet_manage/components/sheetClassifyDia.vue';
@@ -634,9 +634,9 @@ export default {
 }
 </script>
 <style lang='scss' scoped>
-@import './css/index.scss';
+@import '../css/index.scss';
 </style>
 
 <style lang="scss">
-@import './css/pub.scss';
+@import '../css/pub.scss';
 </style>

+ 35 - 21
src/views/chartRelevance_manage/mixins/classifyMixin.js

@@ -1,5 +1,5 @@
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi.js';
-import { fittingEquationInterface,statisticFeatureInterface } from '@/api/modules/chartRelevanceApi';
+import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
 import { dataBaseInterface } from '@/api/api.js';
 
 export default {
@@ -117,7 +117,6 @@ export default {
     resetNodeStyle: _.debounce(function(node) {
 			const tree = $('.target_tree')[0];
 			let width = tree.offsetWidth;
-			// let label_wid = width > 500 ? 'auto' : (width <= 300 && width < 360) ?  100 : 0.7*width;
 			let label_wid =
 				width > 500
 					? 'auto'
@@ -143,26 +142,34 @@ export default {
       if(!this.new_label) return this.$message.warning('名称不能为空');
       this.$set(data,'isEdit',false)
 
-      if(this.new_label !== data.ClassifyName) {
-        let res = this.$route.path === '/fittingEquationList'
-          ? await fittingEquationInterface.classifyEdit({
-              ChartClassifyId: data.ChartClassifyId,
-              ChartClassifyName: this.new_label
-            })
-          : this.$route.path === '/statisticFeatureList'
-          ? await statisticFeatureInterface.classifyEdit({
-              ChartClassifyId: data.ChartClassifyId,
-              ChartClassifyName: this.new_label
-            })
-          :await chartRelevanceApi.classifyEdit({
-              ChartClassifyId: data.ChartClassifyId,
-              ChartClassifyName: this.new_label
-            })
-
-        if(res.Ret !== 200) return
-  
-        this.getTreeData();
+      if(this.new_label === data.ClassifyName) return 
+      
+      let res;
+      if(this.$route.path === '/fittingEquationList') {
+        res = await fittingEquationInterface.classifyEdit({
+                ChartClassifyId: data.ChartClassifyId,
+                ChartClassifyName: this.new_label
+              })
+      }else if(this.$route.path === '/statisticFeatureList') {
+        res = await statisticFeatureInterface.classifyEdit({
+                ChartClassifyId: data.ChartClassifyId,
+                ChartClassifyName: this.new_label
+              })
+      }else if(this.$route.path === '/chartrelevance') {
+        res = await chartRelevanceApi.classifyEdit({
+                ChartClassifyId: data.ChartClassifyId,
+                ChartClassifyName: this.new_label
+              })
+      }else if(this.$route.path === '/crossVarietyChartList') {
+        res = await crossVarietyInterface.classifyEdit({
+          ChartClassifyId: data.ChartClassifyId,
+          ChartClassifyName: this.new_label
+        })
       }
+
+      if(res.Ret !== 200) return
+
+      this.getTreeData();
     },
 
     /* 拖拽完成 */
@@ -214,6 +221,8 @@ export default {
         res=await fittingEquationInterface.classifyMove(params)
       }else if(this.$route.path === '/statisticFeatureList') {
         res=await statisticFeatureInterface.classifyMove(params)
+      }else if(this.$route.path === '/crossVarietyChartList') {
+        res=await crossVarietyInterface.classifyMove(params)
       }
 
       if (res.Ret !== 200) return;
@@ -264,6 +273,8 @@ export default {
         res=await fittingEquationInterface.chartMove(params)
       }else if(this.$route.path === '/statisticFeatureList') {
         res=await statisticFeatureInterface.chartMove(params)
+      }else if(this.$route.path === '/crossVarietyChartList') {
+        res=await crossVarietyInterface.chartMove(params)
       }
       
       if (res.Ret !== 200) return;
@@ -335,6 +346,9 @@ export default {
           (dropNode.level === 2 && type !== "inner")
         ) {
           canDrop = true;
+          
+          //跨品种分析不允许跨分类拖
+          if(draggingNode.data.ChartClassifyId !== dropNode.data.ChartClassifyId)  canDrop = false;
         }
       }
 

+ 4 - 4
src/views/chartRelevance_manage/list.vue → src/views/chartRelevance_manage/relevance/list.vue

@@ -302,7 +302,7 @@
 
 <script>
 import chartRelevanceApi from "@/api/modules/chartRelevanceApi.js";
-import leftMixin from "./mixins/classifyMixin";
+import leftMixin from "../mixins/classifyMixin";
 import Chart from "@/views/dataEntry_manage/components/chart";
 import changeLang from "@/views/dataEntry_manage/components/changeLang.vue";
 import classifyDia from "@/views/datasheet_manage/components/sheetClassifyDia.vue";
@@ -745,10 +745,10 @@ export default {
   },
 };
 </script>
-<style lang='scss' scoped>
-@import './css/index.scss';
+<style lang="scss" scoped>
+@import '../css/index.scss';
 </style>
 
 <style lang="scss">
-@import './css/pub.scss';
+@import '../css/pub.scss';
 </style>

+ 5 - 5
src/views/chartRelevance_manage/relevanceChartEditor.vue → src/views/chartRelevance_manage/relevance/relevanceChartEditor.vue

@@ -373,12 +373,12 @@ import { dataBaseInterface } from '@/api/api.js';
 import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js';
 import {yearSelector} from '@/utils/defaultOptions';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi';
-import selectTarget from './components/selectTarget.vue'
-import chartCard from './components/chartCard.vue';
+import selectTarget from '../components/selectTarget.vue'
+import chartCard from '../components/chartCard.vue';
 import SaveChartOther from '@/views/dataEntry_manage/components/SaveChartOther';
-import saveChartToBase from './components/saveChartTobaseDia.vue';
-import saveEdbToBase from './components/saveEdbToBaseDia.vue'
-import ExplainDialog from './components/explainDialog.vue';
+import saveChartToBase from '../components/saveChartTobaseDia.vue';
+import saveEdbToBase from '../components/saveEdbToBaseDia.vue'
+import ExplainDialog from '../components/explainDialog.vue';
 export default {
   components: { selectTarget, chartCard, SaveChartOther, saveChartToBase, saveEdbToBase, ExplainDialog },
   directives: {

+ 5 - 5
src/views/chartRelevance_manage/statisticFeatureChartEditor.vue → src/views/chartRelevance_manage/statistic/statisticFeatureChartEditor.vue

@@ -289,12 +289,12 @@ import { dataBaseInterface } from '@/api/api.js';
 import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js';
 import {yearSelector} from '@/utils/defaultOptions';
 import {statisticFeatureInterface} from '@/api/modules/chartRelevanceApi';
-import selectTarget from './components/selectTarget.vue'
-import chartCard from './components/chartCard.vue';
+import selectTarget from '../components/selectTarget.vue'
+import chartCard from '../components/chartCard.vue';
 import SaveChartOther from '@/views/dataEntry_manage/components/SaveChartOther';
-import saveChartToBase from './components/saveChartTobaseDia.vue';
-import saveEdbToBase from './components/saveEdbToBaseDia.vue';
-import ExplainDialog from './components/explainDialog.vue';
+import saveChartToBase from '../components/saveChartTobaseDia.vue';
+import saveEdbToBase from '../components/saveEdbToBaseDia.vue';
+import ExplainDialog from '../components/explainDialog.vue';
 export default {
   components: { selectTarget,chartCard,SaveChartOther,saveChartToBase,saveEdbToBase,ExplainDialog },
   directives: {

+ 3 - 3
src/views/chartRelevance_manage/statisticFeatureList.vue → src/views/chartRelevance_manage/statistic/statisticFeatureList.vue

@@ -229,7 +229,7 @@
 <script>
 
 import { statisticFeatureInterface } from '@/api/modules/chartRelevanceApi';
-import leftMixin from './mixins/classifyMixin';
+import leftMixin from '../mixins/classifyMixin';
 import Chart from '@/views/dataEntry_manage/components/chart';
 import classifyDia from '@/views/datasheet_manage/components/sheetClassifyDia.vue';
 import addMyClassifyDia from '@/views/dataEntry_manage/components/addMyClassifyDia';
@@ -602,9 +602,9 @@ export default {
 }
 </script>
 <style lang='scss' scoped>
-@import './css/index.scss';
+@import '../css/index.scss';
 </style>
 
 <style lang="scss">
-@import './css/pub.scss';
+@import '../css/pub.scss';
 </style>

+ 2 - 1
src/views/dataEntry_manage/addChart.vue

@@ -152,6 +152,7 @@
 					></el-date-picker>
 					<el-checkbox label="跨年" v-model="isSpanYear" :disabled="isSpanYearDisable"
 					style="margin-top: 12px;" @change="changeIsSpanYear"></el-checkbox>
+					<br />
 					<div class="legend-set-text" @click="openLegendEditDia">
 						<span>图例名称设置</span>
 						<img src="~@/assets/img/icons/edit-blue.png" />
@@ -835,7 +836,7 @@ export default {
 				.legend-set-text{
 					font-size: 15px;
 					color: #1B7BDE;
-					display: flex;
+					display: inline-flex;
 					align-items: center;
 					justify-content: flex-start;
 					margin-top: 20px;

+ 8 - 2
src/views/dataEntry_manage/components/LegendEditDia.vue

@@ -48,8 +48,14 @@ data () {
   };
 },
 watch: {
-  isEditLegend(val) {
-    if(val) {
+  // isEditLegend(val) {
+  //   if(val) {
+  //     // 数据没加载出来时 打开弹窗 legendListCopy 会为空
+  //     this.legendListCopy = _.cloneDeep(this.legendList)
+  //   }
+  // },
+  legendList(val){
+    if(val){
       this.legendListCopy = _.cloneDeep(this.legendList)
     }
   }

+ 8 - 2
src/views/dataEntry_manage/components/SaveChartOther.vue

@@ -25,7 +25,7 @@
           clearable
         />
       </el-form-item>
-      <el-form-item label="图表分类" prop="classify">
+      <el-form-item label="图表分类" prop="classify" v-if="![10,'cross_variety'].includes(this.source)">
         <el-cascader
           v-model="form.classify"
           :options="classifyOptions"
@@ -53,7 +53,7 @@
 import { dataBaseInterface } from '@/api/api.js';
 import futuresInterface from '@/api/modules/futuresBaseApi';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi.js';
-import { fittingEquationInterface,statisticFeatureInterface } from '@/api/modules/chartRelevanceApi';
+import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
 export default {
   props: {
     show: Boolean,
@@ -114,6 +114,8 @@ export default {
         res=await fittingEquationInterface.classifyList()
       }else if([7,8,9,'statistic_feature'].includes(this.source)) { //标准差 百分位 频率图
         res=await statisticFeatureInterface.classifyList()
+      }else if([10,'cross_variety'].includes(this.source)) { //跨品种分析
+        res = await crossVarietyInterface.classifyList();
       }else{
         res=await dataBaseInterface.chartClassify();
       }
@@ -128,6 +130,8 @@ export default {
         this.filterNodes(res.Data.AllNodes,1)
       }else if([7,8,9,'statistic_feature'].includes(this.source)) { //标准差 百分位 频率图
         this.filterNodes(res.Data.AllNodes,1)
+      }else if([10,'cross_variety'].includes(this.source)) { //
+        this.filterNodes(res.Data.AllNodes,1)
       }else{
         this.filterNodes(res.Data.AllNodes,3)
       }
@@ -163,6 +167,8 @@ export default {
         res=await fittingEquationInterface.saveOtherChart(params)
       }else if([7,8,9,'statistic_feature'].includes(this.source)) { //标准差 百分位 频率图
         res=await statisticFeatureInterface.saveOtherChart(params)
+      }else if([10,'cross_variety'].includes(this.source)) { //跨品种分析
+        res = await crossVarietyInterface.saveOtherChart(params);
       }else{
         res=await dataBaseInterface.saveChartOther(params)
       }

+ 41 - 10
src/views/dataEntry_manage/components/addTarget.vue

@@ -15,16 +15,22 @@
 			<span style="fontSize:16px;">{{dialogTitle}}</span>
 		</div>
 		<div class="dialog-min" style="height:460px;">
-			<div style="overflowY:auto;overflowX:hidden;">
-				<el-tree
-				class="type-list"
-				:data="typeList"
-				accordion
-				highlight-current
-				:props="defaultProps" 
-				@node-click="chooseType"
-				style="width:140px;">
-				</el-tree>
+			<div class="left-cont">
+				<div style="overflowY:auto;overflowX:hidden;">
+					<el-tree
+					class="type-list"
+					:data="typeList"
+					accordion
+					highlight-current
+					:props="defaultProps" 
+					@node-click="chooseType"
+					style="width:140px;">
+					</el-tree>
+				</div>
+				<div class="data-authority-row" @click="dataAuthorityJump">
+					<img src="~@/assets/img/icons/add_blue_new.png" /> 
+					<span>添加手工指标分类</span>
+				</div>
 			</div>
 			<div class="rigth-cont">
 				<el-form
@@ -377,6 +383,10 @@ import {dataInterence} from 'api/api.js';
 					this.classify_id = item.ClassifyId;
 				}
 			},
+			// 手工数据权限页面跳转
+			dataAuthorityJump(){
+				this.$router.push('Sysdatauth')
+			}
 		},
 		created() {
 			// this.getTargetlist();
@@ -393,6 +403,27 @@ import {dataInterence} from 'api/api.js';
 .dialog_container {
 	.dialog-min {
 		display: flex;
+		.left-cont{
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+			padding-bottom: 15px;
+			.data-authority-row{
+				margin-top: 10px;
+				display: inline-flex;
+				align-items: center;
+				cursor: pointer;
+				img{
+					margin-right: 6px;
+					height: 17px;
+					width: 17px;
+				}
+				span{
+					color: #1146DB;
+					font-size: 12px;
+				}
+			}
+		}
 		.rigth-cont {
 			border-left: 1px solid #dcdcdc;
 			padding:  0 0 48px 60px;

+ 7 - 1
src/views/dataEntry_manage/components/insertData.vue

@@ -138,7 +138,13 @@ export default {
     },
     // 上传成功之后
     handleSuccess(result) {
-      let res = this.$parseData(result);
+      //兼容下结构
+      let res = this.$parseData({
+        headers: {
+          dk: sessionStorage.getItem('dk')||""
+        },
+        data: result
+      });
       if (res.Ret === 200) {
         // 0成功 1部分失败 -1全部失败
         let str = `

+ 46 - 12
src/views/dataEntry_manage/components/setEnNameDia.vue

@@ -1,6 +1,14 @@
 <template>
-  <el-dialog title="英文名称" :visible.sync="isOpenDialog" :close-on-click-modal="false" :modal-append-to-body='false'
-       @close="cancel" custom-class="dialog" width="800px" v-dialogDrag>
+  <el-dialog 
+    title="英文名称" 
+    :visible.sync="isOpenDialog" 
+    :close-on-click-modal="false" 
+    :modal-append-to-body='false'
+    @close="cancel" 
+    custom-class="set-en-dialog" 
+    width="800px" 
+    v-dialogDrag
+  >
       <el-form v-if="cType=='chart'" style="padding:0 40px" label-width="140px">
         <!-- 截面散点图 -->
         <div class="set-en-name-box" v-if="chartType===10">
@@ -74,21 +82,17 @@
             </el-collapse-item>
           </el-collapse>
         </div>
-				<el-form-item style="text-align: center;margin-top: 30px;">
-					<el-button type="primary" @click="saveEnName">保存</el-button>
-					<el-button @click="cancel">取消</el-button>
-				</el-form-item>
 			</el-form>
 			<el-form v-else label-width="110px" >
 				<el-form-item :label="item.label+':'" :prop="item.key" v-for="item in copyFormData" :key="item.key">
 					<span v-show="item.notEdit">{{item.value}}</span>
           <el-input v-show="!item.notEdit" v-model="item.value" :placeholder="item.placeholder" style="width: 100%;"></el-input>
 				</el-form-item>
-				<el-form-item style="text-align: center;margin-left:-110px">
-					<el-button @click="cancel">取消</el-button>
-					<el-button type="primary" @click="saveEnName">确定</el-button>
-				</el-form-item>
 			</el-form>
+      <div class="btn-bottom">
+        <el-button type="primary" @click="saveEnName" style="margin-right:20px">确定</el-button>
+        <el-button @click="cancel">取消</el-button>
+      </div>
 		</el-dialog>
 </template>
 <script>
@@ -185,8 +189,29 @@ export default {
       this.$emit('updateEnName',params)
     },
 
+    /* 跨品种分析参数设置 */
+    setCrossVarietyEn() {
+      console.log(this.copyFormData)
+      const { chartInfo,chartsList }  = this.copyFormData;
+      let params = {
+        ChartInfoId: chartInfo[1].id,
+        ChartNameEn: chartInfo[1].value.trim(),
+        TagList: [
+          { ChartTagId: chartInfo[3].id,TagNameEn: chartInfo[3].value },
+          { ChartTagId: chartInfo[5].id,TagNameEn: chartInfo[5].value }
+        ],
+        VarietyList: chartsList.map(_ => ({
+          ChartVarietyId: _[1].id,
+          VarietyNameEn: _[1].value
+        }))
+      }
+      this.$emit('updateEnName',params)
+    },
+
     saveEnName(){
       if(this.cType === 'chart'&&this.chartType === 10) return this.setSectionScatterEn();
+      else if(this.cType === 'chart'&&this.copyFormData.chartInfo[0].source === 10) return this.setCrossVarietyEn();
+
       if(JSON.stringify(this.copyFormData) === JSON.stringify(this.formData)){
         // 没有修改过
         this.$emit('cancel')
@@ -230,9 +255,13 @@ export default {
 }
 </script>
 <style lang="scss">
+.set-en-dialog {
+  .el-dialog__body {
+    max-height:600px;
+    overflow-y:auto;
+  }
+}
 .set-en-name-box{
-  max-height:600px;
-  overflow-y:auto;
   .el-collapse-item__header{
     background-color:#DCDFE6;
     padding-left:8px;
@@ -275,4 +304,9 @@ export default {
       .el-input {  min-width: 240px;}
     }
   }
+  .btn-bottom {
+    display: flex;
+    margin: 40px 0;
+    justify-content: center;
+  }
 </style>

+ 113 - 117
src/views/dataEntry_manage/databaseComponents/addTargetDiaBase.vue

@@ -15,8 +15,8 @@
 							<el-option v-for="item in fromArr" :key="item" :label="item" :value="item">
 							</el-option>
 						</el-select>
-						<el-select v-model="fromDatabase" placeholder="请选择数据库" style="width:240px;margin-left:10px" @change="changeDatabase"
-							:disabled="haveResult" v-if="fromType=='wind'">
+						<el-select v-model="fromDatabase" placeholder="请选择数据库" style="width:240px;margin-left:10px"
+							:disabled="haveResult" v-if="hasDateSequence">
 							<el-option v-for="item in databaseType" :key="item.value" :label="item.label" :value="item.value">
 							</el-option>
 						</el-select>
@@ -26,32 +26,34 @@
 						<i slot="prefix" class="el-input__icon el-icon-search"></i>
 					</el-input>
 
-					<el-input placeholder="指标ID" v-model="search_txt" style="maxWidth:300px" v-if="!(this.fromType=='wind' && this.fromDatabase=='1')"
+					<el-input placeholder="指标ID" v-model="search_txt" style="maxWidth:300px" v-if="!(hasDateSequence && fromDatabase=='1')"
 						@keyup.enter.native="searchHandle" :disabled="haveResult" clearable>
 						<i slot="prefix" class="el-input__icon el-icon-search"></i>
 					</el-input>
 				</div>
-				<div v-if="this.fromType=='wind' && this.fromDatabase=='1'">
+				<div v-if="hasDateSequence && fromDatabase=='1'">
 					<div class="wsd-index-box">
 						<el-input placeholder="请输入证券代码,每次只查询一个证券代码" v-model.trim="securityCodeText" class="wsd-index-input" 
 						@blur="codeInputBlur('security')" style="margin-bottom: 20px;"></el-input>
-						<!-- <div class="wsd-code-row">
-							<div class="wsd-code-item" v-for="item in securityCode" :key="item">
-								<div class="wsd-code-item-text">{{ item }}</div>
-								<img src="~@/assets/img/icons/close_icon_black.png" @click="deleteCode('security',item)" />
-							</div>
-						</div> -->
 						<div class="wsd-index-code">
-							<el-input placeholder="请输入指标代码,多个指标代码用英文逗号分隔" v-model.trim="indexCodeText" class="wsd-index-input" @blur="codeInputBlur('index')"></el-input>
-							<div class="index-code-hint">Wind金融终端输入“CG”会弹出代码生成器,可在代码生成器上获取其他指标的代码</div>
+							<el-input placeholder="请输入指标代码,多个指标代码用英文逗号分隔" v-model.trim="indexCodeText" 
+							class="wsd-index-input" @blur="codeInputBlur('index')"></el-input>
+							<div class="index-code-hint">
+								{{ indexCodeHintText }}
+							</div>
 						</div>
+						<el-select v-model="THSIndexCodeType" placeholder="请选择常用指标类型" v-if="fromType=='同花顺'"
+						@change="THSIndexCodeTypeChange" style="margin-bottom: 15px;" class="wsd-index-input">
+							<el-option :label="item.label" :value="item.value" 
+							v-for="item in THSIndexCodeTypeArr" :key="item.value"></el-option>
+						</el-select>
 						<el-checkbox-group v-model="indexCodeSelected">
 							<el-checkbox :label="item.value" v-for="item in indexCodeArr" :key="item.value">{{ item.label }}</el-checkbox>
 						</el-checkbox-group>
 						<div class="wsd-code-row" style="margin-top: -10px;">
 							<div class="wsd-code-item" v-for="item in indexCode" :key="item">
 								<div class="wsd-code-item-text">{{ item }}</div>
-								<img src="~@/assets/img/icons/close_icon_black.png" @click="deleteCode('index',item)" />
+								<img src="~@/assets/img/icons/close_icon_black.png" @click="deleteCode(item)" />
 							</div>
 						</div>
 					</div>
@@ -110,7 +112,8 @@
                 <td class="sticky" style="left: 0;text-align: center;">
                   {{ edbTableHeadData.get(item) }}
                 </td>
-                <td v-for="(data, sub_index) in edbIndexDatas" :key="sub_index" :colspan="2">
+                <td v-for="(data, sub_index) in edbIndexDatas" :key="sub_index" :colspan="2" 
+								:class="highLightIndex.includes(data.EdbName) && item=='EdbName' ?'exist-highlight':''">
 									<template v-if="item === 'ClassifyId'">
 										<el-cascader :options="options" v-model="data[item]" placeholder="请选择所属目录"
 										size="mini" :disabled="!data.Source"
@@ -172,8 +175,8 @@
 			<div class="check-fail-box">
 				<div>
 					<div style="margin-bottom: 20px;">指标库中已存在以下指标,请勿重新输入!</div>
-					<div v-for="(item,index) in existIndexList">
-						{{ index+1+'、'+item }}
+					<div v-for="(item,index) in existIndexList" class="exist-index-item" @click="existIndexClick(item)">
+						{{ index+1+'、'+item.text }}
 					</div>
 				</div>
 				<div class="check-fail-button">
@@ -186,7 +189,7 @@
 
 <script>
 import { dataBaseInterface,dataInterence } from '@/api/api.js'
-import { fromArr, fromCode ,frequencyArr} from './util';
+import { frequencyArr,windCommonIndexCodeArr,THSCommonIndexStockCodeArr,THSCommonIndexFuturesCodeArr} from './util';
 export default {
 	name: '',
 	props: {
@@ -196,39 +199,50 @@ export default {
 	},
 	computed:{
 		isCodeComplete(){
-			// return this.securityCode.length>0 && (this.indexCode.length>0 || this.indexCodeSelected.length>0)
 			return this.securityCodeText && (this.indexCode.length>0 || this.indexCodeSelected.length>0)
-			
+		},
+		hasDateSequence(){
+			return ["wind","同花顺"].includes(this.fromType)
+		},
+		// 常用的指标代码
+		indexCodeArr(){
+			if(this.fromType=='wind'){
+				return windCommonIndexCodeArr
+			}else if(this.fromType=='同花顺'){
+				if(this.THSIndexCodeType==1){
+					//期货常用
+					return THSCommonIndexFuturesCodeArr
+				}else{
+					//股票常用
+					return THSCommonIndexStockCodeArr
+				}
+			}
+		},
+		indexCodeHintText(){
+			if(this.fromType=='wind'){
+				return 'Wind金融终端输入“CG”会弹出代码生成器,可在代码生成器上获取其他指标的代码'
+			}else if(this.fromType=='同花顺'){
+				return '可用Excel同花顺插件/日期序列功能,根据所选指标获取指标代码,期货和股票常用代码可在下方勾选'
+			}
 		}
 	},
 	data() {
+		this.prefixMap=new Map([
+			["wind","wsd"],
+			["同花顺","thsds"]
+		])
 		return {
 			search_txt: '',//搜素关键词 M001625518 M0001427
 			fromType: 'wind',
 			fromDatabase:'0',
-			fromArr,
+			fromArr:[],
 			indexCodeSelected:[],
-			indexCodeArr:[
-				{value:'pre_close',label:"前收盘价"},
-				{value:'open',label:"开盘价"},
-				{value:'high',label:"最高价"},
-				{value:'low',label:"最低价"},
-				{value:'close',label:"收盘价"},
-				{value:'settle',label:"结算价"},
-				{value:'volume',label:"成交量"},
-				{value:'amt',label:"成交额"},
-				{value:'pct_chg',label:"涨跌幅"},
-				{value:'swing',label:"振幅"},
-				{value:'turn',label:"换手率"},
-				{value:'oi',label:"持仓量"},
-				{value:'oiamount',label:"持仓额"},
-			], // 常用的指标代码
 			databaseType:[
 				{value:'0',label:'经济数据库'},
 				{value:'1',label:'日期序列'}
 			],
 			wsdAddStep:1,
-			fromCode,
+			fromCode:[],
 			tableColums: [
 				{
 					label: '指标ID',
@@ -255,35 +269,9 @@ export default {
 			isCompanyCode: false,
 			search_company_txt: '',
 			securityCodeText:"",
-			// securityCode:[],
 			indexCodeText:'',
 			indexCode:[],
-			edbIndexDatas:[
-				// {Source:1,Catalogue:'',Unit:"",Frequency:'日度',SecName:'CU2403收盘价',SecurityCode:'CU2403.SHF',IndexCode:'close',
-				// DataList:[{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"}]},
-				// {Catalogue:'',Unit:"",Frequency:'日度',SecName:'CU2403收盘价',SecurityCode:'CU2403.SHF',IndexCode:'close',
-				// DataList:[{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"}]},
-				// {Catalogue:'',Unit:"",Frequency:'日度',SecName:'CU2403收盘价',SecurityCode:'CU2403.SHF',IndexCode:'close',
-				// DataList:[{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"}]},
-				// {Catalogue:'',Unit:"",Frequency:'日度',SecName:'CU2403收盘价',SecurityCode:'CU2403.SHF',IndexCode:'close',
-				// DataList:[{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"}]},
-				// {Catalogue:'',Unit:"",Frequency:'日度',SecName:'CU2403收盘价',SecurityCode:'CU2403.SHF',IndexCode:'close',
-				// DataList:[{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"}]},
-				// {Catalogue:'',Unit:"",Frequency:'日度',SecName:'CU2403收盘价',SecurityCode:'CU2403.SHF',IndexCode:'close',
-				// DataList:[{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},
-				// {Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"},{Close: "632107.0000",Dt: "2023-09-07"}]}
-			],
+			edbIndexDatas:[],
 			edbTableHeadKey: [
         "ClassifyId",
         "Unit",
@@ -306,7 +294,14 @@ export default {
 			frequencyArr,
 			checkFailShow:false,
 			existIndexList:[],
-			isLoadingData:false
+			isLoadingData:false,
+			highLightIndex:[],
+			// 同花顺常用指标类型
+			THSIndexCodeType:1,
+			THSIndexCodeTypeArr:[
+				{value:1,label:"期货常用指标"},
+				{value:2,label:"股票常用指标"}
+			]
 		};
 	},
 	watch:{
@@ -322,7 +317,6 @@ export default {
 		init() {
 			this.fromType = 'wind'
 			this.fromDatabase='0'
-			this.wsdAddStep=1
 			this.status = ''
 			this.tableData = []
 			this.dataList = []
@@ -330,6 +324,12 @@ export default {
 			this.search_txt = '';
 			this.haveResult = false;
 			this.search_company_txt = '';
+			this.wsdAddStep=1
+			this.securityCodeText=""
+			this.indexCodeText=''
+			this.indexCode=[]
+			this.indexCodeSelected=[]
+			this.THSIndexCodeType=1
 			this.isCompanyCode = false;
 		},
 		cancelHandle() {
@@ -374,23 +374,26 @@ export default {
 					if (res.Ret === 200) {
 						this.haveResult = false;
 						//隆众指标和手工指标、交易所等将名称 频度 单位带到下一步
-						let opther_params = ['9', '10', '11', '15', '16', '17', '18', '19', '20', '21', '26','38','41','57','60','71'].includes(this.fromCode.get(this.fromType)) ? {
-							edb_name: res.Data.SearchItem.EdbName,
-							unit: res.Data.SearchItem.Unit,
-							frequency: res.Data.SearchItem.Frequency,
-						} : {}
-						let jy_params = Number(this.fromCode.get(this.fromType))>1000?{
-							edb_name:res.Data.SearchItem.EdbName,
-							unit:res.Data.SearchItem.Unit,
-							frequency:frequencyArr.includes(res.Data.SearchItem.Frequency)?res.Data.SearchItem.Frequency:''
-						}:{}
+						console.log(this.fromCode.get(this.fromType),'this.fromCode.get(this.fromType)');
+						// let opther_params = ['9', '10', '11', '15', '16', '17', '18', '19', '20', '21', '26','38','41','57','60','71'].includes(this.fromCode.get(this.fromType)) ? 
+						let opther_params={
+							edb_name: res.Data.SearchItem.EdbName || '',
+							unit: res.Data.SearchItem.Unit || '',
+							frequency: frequencyArr.includes(res.Data.SearchItem.Frequency)?res.Data.SearchItem.Frequency:'',
+						} 
+						// : {}
+						// let jy_params = Number(this.fromCode.get(this.fromType))>1000?{
+						// 	edb_name:res.Data.SearchItem.EdbName,
+						// 	unit:res.Data.SearchItem.Unit,
+						// 	frequency:frequencyArr.includes(res.Data.SearchItem.Frequency)?res.Data.SearchItem.Frequency:''
+						// }:{}
 						this.add_params = {
 							EdbCode: res.Data.SearchItem.EdbCode,
 							EndDate: res.Data.SearchItem.EndDate,
 							StartDate: res.Data.SearchItem.StartDate,
 							Source: Number(this.fromCode.get(this.fromType)),
 							...opther_params,
-							...jy_params
+							// ...jy_params
 						}
 						this.status = res.Data.Status;
 						let classify_arr = res.Data.ClassifyList || [];
@@ -434,18 +437,18 @@ export default {
 				this.isCompanyCode = false;
 			}
 			if(this.fromDatabase=='1'){
-				this.changeDatabase('0')
+				this.fromDatabase='0'
+				this.securityCodeText=""
+				this.indexCodeText=''
+				this.indexCode=[]
+				this.indexCodeSelected=[]
+				this.THSIndexCodeType=1
 			}
 			this.search_txt = '';
 			this.search_company_txt = '';
 			this.loading && this.loading.close();
 		},
-		changeDatabase(e){
-			this.fromDatabase=e
-			this.securityCodeText=""
-			// this.securityCode=[]
-			this.indexCodeText=''
-			this.indexCode=[]
+		THSIndexCodeTypeChange(){
 			this.indexCodeSelected=[]
 		},
 		getTargetSource(){
@@ -466,18 +469,6 @@ export default {
 		codeInputBlur(type){
 			if(type == 'security'){
 				this.securityCodeText=this.securityCodeText.replaceAll(' ','')
-				// if(!this.securityCodeText) return  
-				// if(!(this.securityCode.length<5)){
-				// 	this.$message.warning('证券代码一次最多输入5个')
-				// 	this.securityCodeText=''
-				// 	return
-				// }
-				// this.securityCode=Array.from(new Set([...this.securityCode,...this.securityCodeText.split(',')])).filter(Boolean)
-				// if(this.securityCode.length>5){
-				// 	this.securityCode = this.securityCode.slice(0,5)
-				// 	this.$message.warning('证券代码一次最多输入5个')
-				// }
-				// this.securityCodeText=''
 			}else if(type == 'index'){
 				if(!this.indexCodeText) return  
 				this.indexCodeText=this.indexCodeText.replaceAll(' ','')
@@ -487,12 +478,8 @@ export default {
 			}
 
 		},
-		deleteCode(type,code){
-			if(type == 'security'){
-				// this.securityCode = this.securityCode.filter(it => it!=code)
-			}else if(type == 'index'){
-				this.indexCode = this.indexCode.filter(it => it!=code)
-			}
+		deleteCode(code){
+			this.indexCode = this.indexCode.filter(it => it!=code)
 		},
 		/* 获取目录结构 */
 		getMenu() {
@@ -535,28 +522,21 @@ export default {
 						Source:Number(this.fromCode.get(this.fromType)),
 						SubSource:this.fromDatabase,
 						EdbCode:Array.from(new Set([...this.indexCode,...this.indexCodeSelected])).join(','),
-						// StockCode:this.securityCode.join(',')
 						StockCode:this.securityCodeText
 					}
 					dataBaseInterface.edbExistCheck(params).then(res=>{
 						if(res.Ret == 200){
+							// console.log(res,'res');
+							// return 
 							if(res.Data.IndexExist){
 								// 有重复
 								this.existIndexList=[]
-								let ExistEdbCodeArr=res.Data.ExistEdbCodeArr
-								// let ExistStockCodeArr=res.Data.ExistStockCodeArr
+								let existEdbInfo=res.Data.ExistEdbInfo || []
 								let text=''
-								// ExistStockCodeArr.map(item =>{
-									Array.from(new Set(ExistEdbCodeArr)).map(item1 =>{
-										let isCommon = this.indexCodeArr.find(it => it.value == item1)
-										if(isCommon){
-											text=`${this.securityCodeText}${isCommon.label}(wsd${this.securityCodeText}${isCommon.value})`
-										}else{
-											text=`${this.securityCodeText}${item1}(wsd${this.securityCodeText}${item1})`
-										}
-										this.existIndexList.push(text)
-									})
-								// })
+								existEdbInfo.map(item =>{
+									text=`${item.EdbName}(${item.EdbCode})`
+									this.existIndexList.push({text,code:item.UniqueCode,id:item.EdbInfoId,classifyId:item.ClassifyId})
+								})
 								this.checkFailShow=true
 							}else{
 								// 没有重复
@@ -614,6 +594,10 @@ export default {
 				}
 			},50)
 		},
+		existIndexClick({code,id,classifyId}){
+			const { href } = this.$router.resolve({ path: '/database',query:{code,id,classifyId}});
+			window.open(href, '_blank');
+		},
 		wsdPrevHandle(){
 			this.wsdAddStep=1
 		},
@@ -638,6 +622,8 @@ export default {
 					this.$message.success("添加指标成功")
 					this.$emit('addSuccessHandle',{ code:res.Data.UniqueCode,id:res.Data.EdbInfoId,classifyId:res.Data.ClassifyId })	
 					this.cancelHandle()	
+				}else if(res.Ret == 403){
+					this.highLightIndex=res.Data?res.Data.ExistEdbName || []:[]
 				}
 			})
 		}
@@ -695,7 +681,7 @@ export default {
 		.wsd-index-code{
 			display: flex;
 			align-items: center;
-			margin-bottom: 15px;
+			margin-bottom: 20px;
 			.index-code-hint{
 				color: #C0C4CC;
 				font-size: 15px;
@@ -728,7 +714,8 @@ export default {
 			}
 		}
 		.wsd-index-input{
-			width: 400px;
+			min-width: 400px;
+			width:400px;
 			margin-right: 10px;
 		}
 	}
@@ -806,10 +793,19 @@ export default {
       border-top: 1px solid #dcdfe6;
       border-left: 1px solid #dcdfe6;
     }
+		.exist-highlight{
+			border: red solid 1px;
+		}
 	}
 	.check-fail-box{
 		padding: 0 0 15px 40px;	
 		color: #333333;
+		.exist-index-item{
+			cursor: pointer;
+			&:hover{
+				text-decoration: underline;
+			}
+		}
 		.check-fail-button{
 			margin-top: 80px;
 			display: flex;

+ 261 - 42
src/views/dataEntry_manage/databaseComponents/computedDialog.vue

@@ -70,15 +70,99 @@
 				添加更多参数
 			</span>
 			<div class="computed-min">
-				<div class="computed-top">
-					<span style="margin-right: 8px">计算公式</span>
-					<el-input placeholder="请输入公式" v-model="formula" clearable :disabled="calulateForm.view">
-					</el-input>
+				<div class="computed-section">
+					<div>
+						<label class="label">空值处理
+							<el-tooltip placement="top">
+								<div slot="content" v-html="formTips['null-val']" style="width:300px;line-height:20px;"/>
+								<i class="el-icon-question"/>
+							</el-tooltip>
+						</label>
+						<el-select
+							v-model="nullValueForm.nullValueWay"
+							placeholder="请选择"
+							:disabled="calulateForm.view"
+						>
+							<el-option
+								v-for="item in nullWayOptions"
+								:key="item.value"
+								:label="item.label"
+								:value="item.value"
+							>
+							</el-option>
+						</el-select>
+					</div>
+
+					<div style="margin-left: 120px" v-if="showMaxNullDeal">
+						<label class="label">MAX、MIN空值处理
+							<el-tooltip placement="top">
+								<div slot="content" v-html="formTips['max-null-val']" style="width:300px;line-height:20px;"/>
+								<i class="el-icon-question"/>
+							</el-tooltip>
+						</label>
+						<el-select
+							v-model="nullValueForm.maxNullWay"
+							placeholder="请选择"
+							:disabled="calulateForm.view"
+						>
+							<el-option label="等于0" :value="1" />
+							<el-option label="跳过空值" :value="2" />
+						</el-select>
+					</div>
+				</div>	
+				<div class="computed-section">
+					<label class="label">计算公式
+						<el-tooltip placement="top">
+							<div slot="content" v-html="formTips['formula']" style="width:300px;line-height:20px;"/>
+							<i class="el-icon-question"/>
+						</el-tooltip>
+					</label>
+					<!-- <el-input placeholder="请输入公式" v-model="formula" clearable :disabled="calulateForm.view">
+					</el-input> -->
+					<ul class="formula-list">
+						<li style="margin-bottom: 15px;">
+							<el-input placeholder="请输入公式" v-model="formulaList[0].formula" clearable :disabled="calulateForm.view" style="width: 220px"/>
+							
+							<span v-if="formulaDateArr.length" class="date-section-text">{{formulaDateArr[formulaDateArr.length-1]}}(含)之后</span>
+
+							<span class="example-txt">公式示例:A*0.5+B*C*1.2+120-MAX(A,B,C) &nbsp;函数支持:MAX(),MIN(),ln(A),log(a,A),abs(),exp(),pow(),round()</span>
+						</li>
+
+						<li class="formula-item" v-for="(item,index) in formulaList.slice(1)" :key="index+1">
+							<el-input 
+								placeholder="请输入公式" 
+								v-model="item.formula" 
+								clearable 
+								:disabled="calulateForm.view"
+								style="width: 220px"
+							/>
+
+							<el-date-picker
+								v-model="item.date"
+								type="date"
+								value-format="yyyy-MM-dd"
+								style="margin: 0 10px;width: 220px"
+								placeholder="选择日期"
+								:disabled="calulateForm.view"
+								@change="selectFormulaDate($event,item)"
+							/>
+
+							<i class="el-icon-circle-close" style="font-size:20px;" v-if="!calulateForm.view" @click="removeFormulaItem(index+1)"/>
+
+							<template v-if="formulaDateArr.length&&item.date">
+								<span v-if="item.date===formulaDateArr[0]" class="date-section-text">{{formulaDateArr[0]}}之前</span>
+								<span v-else class="date-section-text">{{formulaDateArr[formulaDateArr.findIndex(_ =>_ ===item.date)-1]}}(含)——{{item.date}}</span>
+							</template>
+						</li>
+					</ul>
 				</div>
-				<span class="example-txt"
-					>公式示例:A*0.5+B*C*1.2+120-MAX(A,B,C)</span
-				>
-				<span class="example-txt">函数支持:MAX(),MIN(),ln(A),log(a,A)</span>
+				<el-button 
+					v-if="!calulateForm.view" 
+					icon="el-icon-plus" 
+					style="margin-left:70px;" 
+					@click="addFormulaHandle"
+				>新增分段</el-button>
+
 			</div>
 			<el-form
 				ref="diaForm"
@@ -148,7 +232,7 @@
 			placement="top-start"
 			width="360"
 			trigger="click">
-			<p style="padding:30px;line-height:25px;" v-html="$parent.tips.get(4)"/>
+			<p style="padding:30px;line-height:25px;" v-html="$parent.tips.get(type)"/>
 			<span slot="reference" class="tip-label">公式说明</span>
 		</el-popover>
 	</el-dialog>
@@ -156,6 +240,7 @@
 
 <script>
 import { dataBaseInterface } from '@/api/api.js';
+import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js';
 import { formRules } from '../databaseComponents/util';
 import { unitArr } from '@/utils/defaultOptions';
 const tag_arr = [];
@@ -176,6 +261,26 @@ export default {
 		calulateList: {
 			type: Array,
 		},
+		edbSource: { // ''||'predict'
+			type: String,
+			default: ''
+		}
+	},
+	computed: {
+		type() {
+			return this.edbSource ? 31 : 4;
+		},
+
+		/* max空值处理显示 当输入的公式包含MAX、MIN且空值处理为0时,输入公式失焦后出现右侧选项; */
+		showMaxNullDeal() {
+			let haveMaxOrMin = this.formulaList.some(_ => _.formula.toUpperCase().includes('MAX') || _.formula.toUpperCase().includes('MIN'))
+
+			return haveMaxOrMin && this.nullValueForm.nullValueWay===4
+		},
+
+		formulaDateArr() {
+			return this.formulaList.map(_ => _.date).filter(_ => _).sort((a,b) => new Date(a)-new Date(b))
+		}
 	},
 	watch: {
 		isOpenComputed(newval) {
@@ -184,7 +289,10 @@ export default {
 			/* 回显 */
 			if (this.calulateList.length && newval) {
 				this.addList = _.cloneDeep(this.calulateList);
-				this.formula = this.calulateForm.formula;
+				this.formulaList = JSON.parse(this.calulateForm.formula).map(_ => ({
+					formula: _.f,
+					date: _.d||''
+				}));
 				this.formData = {
 					targetName: this.calulateForm.targetName,
 					unit: this.calulateForm.unit,
@@ -192,6 +300,11 @@ export default {
 					frequency: this.calulateForm.frequency,
 				};
 
+				this.nullValueForm = {
+					nullValueWay: this.calulateForm.emptyType,
+					maxNullWay: this.calulateForm.maxEmptyType,
+				}
+
 				this.searchOptions = this.calulateList.map(item => ({
 					EdbInfoId: item.target,
 					EdbName: item.edb_name,
@@ -241,6 +354,9 @@ export default {
 			],
 			searchOptions: [],
 			formula: '', //计算公式
+			formulaList: [ //公式数组
+				{ formula: '', }
+			],
 			dataloading: false,
 			formData: {
 				targetName: '',
@@ -252,18 +368,46 @@ export default {
 			search_page: 1,
 			current_search:'',
 
-			newestDate: ''
+			newestDate: '',
+
+			nullValueForm: {
+				nullValueWay: 0,
+				maxNullWay: 1
+			},//空值处理
+			nullWayOptions: [
+				{ label: '查找前后35天最近值',value: 0 },
+				{ label: '不计算',value: 1 },
+				{ label: '前值填充',value: 2 },
+				{ label: '后值填充',value: 3 },
+				{ label: '等于0',value: 4 },
+			],
+			formTips: {
+				'null-val': `1、查找前后35天最近值:在参与计算的日期序列上某指标无值时,该指标往前/往后找距离最近的值作为当天的值进行计算,遍历允许跨年,往前最多35天,往后最多35天<br>
+				2、不计算:只要有一个指标在某个日期没有值(即空值),则计算指标在该日期没有值 <br>
+				3、前值填充:空值优先以最近的前值填充,没有前值时,用后值填充 <br>
+				4、后值填充:空值优先以最近的后值填充,没有前值时,用后值填充 <br>
+				5、等于0:空值以0值参与计算 <br>
+				注意:此处缺失值的处理,作用于数据全部时间段`,
+				'max-null-val': `MAX、MIN公式中指标存在空值时按如下规则处理:<br>
+				1、等于0,空值用0参与计算;<br>
+				2、跳过空值,去除空值指标,剩余指标进行计算,若该日期所有指标均为空值,则该日期无值;`,
+				'formula':`1、支持新增分段,实现不同分段使用不同的计算公式,若未新增分段,则所有日期序列用统一公式计算<br>
+				2、新增分段需配置新公式和时间节点,在时间节点之前(不含)使用新公式,在时间节点之后(含)使用已配置公式,每个分段公式支持修改<br>
+				3、分段时间节点不允许重复,不允许超出第一个指标的日期区间`
+			}
+
 		};
 	},
 	methods: {
 		/* 获取目录结构 */
-		getMenu() {
-			dataBaseInterface.menuListV3().then((res) => {
-				if (res.Ret === 200) {
-					this.filterNodes(res.Data.AllNodes||[]);
-					this.options = res.Data.AllNodes || [];
-				}
-			});
+		async getMenu() {
+			const res = this.edbSource === 'predict'
+				? await preDictEdbInterface.classifyListV2()
+				: await dataBaseInterface.menuListV3()
+				if (res.Ret !== 200) return
+				
+				this.filterNodes(res.Data.AllNodes||[]);
+				this.options = res.Data.AllNodes || [];
 		},
 		// 递归改变第三级目录结构
 		filterNodes(arr) {
@@ -302,18 +446,25 @@ export default {
 			this.searchApi(this.current_search);
 		},
 
-		searchApi(query,page=1) {
-			dataBaseInterface.targetSearchByPage({
-				KeyWord:query,
-				CurrentIndex: page
-			}).then(res => {
+		async searchApi(query,page=1) {
+			const res = this.edbSource === 'predict'
+				? await preDictEdbInterface.edbSearch({
+            Keyword: query,
+            CurrentIndex: page,
+						FilterSource: this.type === 45 ? 3 : this.type === 66 ? 6 : 1,
+						Frequency: this.type===64?'季度': ''
+          })
+				: await dataBaseInterface.targetSearchByPage({
+					KeyWord:query,
+					CurrentIndex: page,
+					FilterSource: this.type === 14 ? 3 : this.type === 63 ? 6 : 1,
+					Frequency: this.type===61?'季度': ''
+				})
 				if(res.Ret !== 200) return
 
 				const { List,Paging } = res.Data;
 				this.search_have_more = page < Paging.Pages;
 				this.searchOptions = page === 1 ? List : this.searchOptions.concat(List);
-					
-			})
 		},
 
 		searchLoad() {
@@ -361,30 +512,41 @@ export default {
 			this.getNewestDate();
 		},
 		async saveHandle() {
-			if (!this.formula) return this.$message.warning('计算公式不能为空');
+				if (!this.formulaList[0].formula) return this.$message.warning('计算公式不能为空');
 				await this.$refs.diaForm.validate();
 
 				// 指标id数组
-				let target_arr = this.addList
-					.filter((item) => item.target)
-					.map((item) => {
-						return {
-							EdbInfoId: item.target,
-							FromTag: item.tag,
-						};
-					});
+				let EdbInfoIdArr = this.addList.filter((item) => item.target).map((item) => ({
+						EdbInfoId: item.target,
+						FromTag: item.tag,
+					}));
+
+				let formulaArr = this.formulaList
+					.filter((_,index) => index===0||(index>0&&_.formula&&_.date))
+					.map(_ => ({f: _.formula,d: _.date}))
+
+				const { nullValueWay,maxNullWay } = this.nullValueForm;
+				
 				let params = {
-					CalculateFormula: this.formula,
+					CalculateFormula: JSON.stringify(formulaArr),
 					ClassifyId: this.formData.menu[this.formData.menu.length - 1] || 0,
 					EdbName: this.formData.targetName,
 					Frequency: this.formData.frequency,
 					Unit: this.formData.unit,
-					EdbInfoIdArr: target_arr,
+					EdbInfoIdArr,
+					EmptyType: nullValueWay,
+					MaxEmptyType: maxNullWay
 				};
 				this.dataloading = true;
-				const res = this.calulateForm.edb_id
-					? await dataBaseInterface.calculateEdit({...params,EdbInfoId: this.calulateForm.edb_id})
-					: await dataBaseInterface.calculateAdd(params)
+
+				let res;
+				if(this.edbSource === 'predict') {
+					res = await preDictEdbInterface.calculateEdbSave(this.calulateForm.edb_id ? {...params,EdbInfoId:this.calulateForm.edb_id } : params)
+				}else {
+					res = this.calulateForm.edb_id
+						? await dataBaseInterface.calculateEdit({...params,EdbInfoId: this.calulateForm.edb_id})
+						: await dataBaseInterface.calculateAdd(params)
+				}
 
 				this.dataloading = false;
 				if (res.Ret !== 200) return
@@ -394,6 +556,42 @@ export default {
 						: this.$emit('addCallBack','add',{ code:res.Data.UniqueCode,id:res.Data.EdbInfoId,classifyId:params.ClassifyId });
 				this.init();
 		},
+
+		/* 新增公式分段 */
+		addFormulaHandle() {
+			let addItem = {
+				formula: this.formulaList[this.formulaList.length-1].formula,
+				date: ''
+			}
+			this.formulaList.push(addItem)
+		},
+
+		/* 移除公式 */
+		removeFormulaItem(index) {
+			this.formulaList.splice(index,1)
+		},
+
+		/* 选择日期 检验排 */
+		selectFormulaDate(val,item) {
+			console.log(val,item)
+			const { start_date,end_date } = this.addList[0];
+
+			if(!start_date) return
+
+			let dateStamp = new Date(val).getTime(), 
+					startStamp = new Date(start_date).getTime(),
+					endStamp = new Date(end_date).getTime();
+			if (dateStamp > endStamp || dateStamp < startStamp) {
+				item.date = '';
+				return this.$message.warning('分段日期必须在第一个指标日期区间')
+			}
+
+			else if(this.formulaList.filter(_ => _.date===val).length>1) {
+				item.date = '';
+				return this.$message.warning('分段日期不可重复')
+			}
+		},
+
 		init() {
 			this.$refs.diaForm.resetFields();
 			this.addList = [
@@ -430,6 +628,14 @@ export default {
 				menu: '',
 				frequency: '',
 			};
+			this.formulaList = [
+				{ formula: '' }
+			]
+			this.nullValueForm = {
+				nullValueWay: 0,
+				maxNullWay: 1
+			}
+
 		},
 		cancelHandle(type) {
 			this.init();
@@ -437,7 +643,6 @@ export default {
 			type==='cancel' && !this.calulateForm.edb_id && this.$emit('openPrev');
 		},
 	},
-	created() {},
 	mounted() {},
 };
 </script>
@@ -501,10 +706,24 @@ export default {
 			margin: 50px 0;
 			padding-bottom: 40px;
 			border-bottom: 1px dashed #aab4cc;
+			.computed-section {
+				display: flex;
+				margin-top: 20px;
+			}
+			.label {
+				padding:10px 10px 10px 0;
+			}
 			.example-txt {
 				display: block;
-				margin-left: 70px;
-				margin-top: 15px;
+				margin-top: 10px;
+			}
+			.formula-item {
+				display: flex;
+				align-items: center;
+				margin-bottom: 15px;
+			}
+			.date-section-text {
+				margin-left: 15px;
 			}
 		}
 	}

+ 52 - 3
src/views/dataEntry_manage/databaseComponents/util.js

@@ -155,11 +155,12 @@ export const computedBatchTypes = [
 
 //频度
 export const frequencyArr = ['日度','周度','旬度','月度','季度','年度']
-//添加基础指标来源
+//添加基础指标来源 已改为从接口获取 datamanage/edb_source/list
 export const fromArr = ['wind','同花顺','彭博','彭博财务','路透','手工指标','隆众指标','SMM','Mysteel','郑商所',
 '大商所','上期所','中金所','上期能源','欧洲天然气','中国煤炭市场网','谷歌出行指数','EIA STEO报告','UN','卓创数据(红桃3)',
 '百川盈孚','国家统计局','富宝数据'];
 
+// 已改为从接口获取 datamanage/edb_source/list
 export const fromCode = new Map([
 	['同花顺','1'],
 	['wind','2'],
@@ -229,8 +230,10 @@ export const allFromArr = [
 
 //公式说明 eta指标库
 export const formulaTip = new Map([
-	[4,`指标运算:选择指标参数,按照输入的计算公式进行计算,生成新的指标<br>
-	在参与计算的日期序列上某指标无值时,该指标往前/往后找距离最近的值作为当天的值进行计算,遍历允许跨年,往前最多35天,往后最多35天`],
+	[4,`指标运算:<br>
+	1、选择指标参数<br>
+	2、生成指标的时间序列:以第一个指标为准,其他指标去匹配第一个指标的日期序列,没有值的,按照所选空值处理方式处理<br>
+	3、按照输入的计算公式进行计算`],
 	[5,`累计值转月值计算方法:<br>
 	<p style="display:flex;justify-content: space-between;">
 		<span style="width:45%">1月无值:1月=2月/2</span>
@@ -437,3 +440,49 @@ export const formulaTip = new Map([
 	5、旬度值转日均值=旬度值/对应旬度天数 <br>
 	6、周度值转日均值=周度值/7`]
 ])
+
+// wind 日期序列常见指标代码
+export const windCommonIndexCodeArr=[
+	{value:'pre_close',label:"前收盘价"},
+	{value:'open',label:"开盘价"},
+	{value:'high',label:"最高价"},
+	{value:'low',label:"最低价"},
+	{value:'close',label:"收盘价"},
+	{value:'settle',label:"结算价"},
+	{value:'volume',label:"成交量"},
+	{value:'amt',label:"成交额"},
+	{value:'pct_chg',label:"涨跌幅"},
+	{value:'swing',label:"振幅"},
+	{value:'turn',label:"换手率"},
+	{value:'oi',label:"持仓量"},
+	{value:'oiamount',label:"持仓额"}
+]
+
+// 同花顺 日期序列常见 股票指标代码
+export const THSCommonIndexStockCodeArr=[
+	{value:'ths_pre_close_stock',label:"前收盘价"},
+	{value:'ths_open_price_stock',label:"开盘价"},
+	{value:'ths_high_price_stock',label:"最高价"},
+	{value:'ths_low_stock',label:"最低价"},
+	{value:'ths_close_price_stock',label:"收盘价"},
+	{value:'ths_vol_stock',label:"成交量"},
+	{value:'ths_amt_stock',label:"成交额"},
+	{value:'ths_chg_ratio_stock',label:"涨跌幅"},
+	{value:'ths_swing_stock',label:"振幅"},
+	{value:'ths_turnover_ratio_stock',label:"换手率"},
+]
+
+// 同花顺 日期序列常见 指标代码
+export const THSCommonIndexFuturesCodeArr=[
+	{value:'ths_pre_close_future',label:"前收盘价"},
+	{value:'ths_open_price_future',label:"开盘价"},
+	{value:'ths_high_price_future',label:"最高价"},
+	{value:'ths_low_future',label:"最低价"},
+	{value:'ths_close_price_future',label:"收盘价"},
+	{value:'ths_settle_future',label:"结算价"},
+	{value:'ths_vol_future',label:"成交量"},
+	{value:'ths_amt_future',label:"成交额"},
+	{value:'ths_chg_ratio_future',label:"涨跌幅"},
+	{value:'ths_swing_d_future',label:"日振幅"},
+	{value:'ths_open_interest_future',label:"持仓量"},
+]

+ 8 - 5
src/views/dataEntry_manage/databaseList.vue

@@ -1702,14 +1702,17 @@ export default {
 						end_date: item.EndDate
 					}
 				})
+				const { EdbInfoId,CalculateFormula,EdbName,Unit,Frequency,EmptyType,MaxEmptyType } = res.EdbInfoDetail;
 				/* 公式和表单 */
 				this.calulateForm =  {
-					edb_id:res.EdbInfoDetail.EdbInfoId,
-					formula: res.EdbInfoDetail.CalculateFormula,
+					edb_id: EdbInfoId,
+					formula: CalculateFormula,
 					menu: menuArrId||[],
-					targetName: res.EdbInfoDetail.EdbName,
-					unit: res.EdbInfoDetail.Unit,
-					frequency: res.EdbInfoDetail.Frequency,
+					targetName: EdbName,
+					unit: Unit,
+					frequency: Frequency,
+					emptyType: EmptyType,
+					maxEmptyType: MaxEmptyType,
 					view
 				};
 			} else  {

+ 2 - 1
src/views/dataEntry_manage/editChart.vue

@@ -161,6 +161,7 @@
 					></el-date-picker>
 					<el-checkbox label="跨年" v-model="isSpanYear" :disabled="isSpanYearDisable"
 					style="margin-top: 12px;" @change="changeIsSpanYear"></el-checkbox>
+					<br />
 					<div class="legend-set-text" @click="openLegendEditDia">
 						<span>图例名称设置</span>
 						<img src="~@/assets/img/icons/edit-blue.png" />
@@ -918,7 +919,7 @@ export default {
 				.legend-set-text{
 					font-size: 15px;
 					color: #1B7BDE;
-					display: flex;
+					display: inline-flex;
 					align-items: center;
 					justify-content: flex-start;
 					margin-top: 20px;

+ 209 - 14
src/views/dataEntry_manage/mixins/chartPublic.js

@@ -8,7 +8,7 @@ import { dataBaseInterface } from '@/api/api';
 import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js';
 import futuresInterface from '@/api/modules/futuresBaseApi';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi';
-import { fittingEquationInterface,statisticFeatureInterface } from '@/api/modules/chartRelevanceApi';
+import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
 
 /* 散点x轴 */
 const scatterXAxis = {
@@ -36,7 +36,19 @@ const basicYAxis = {
   showLastLabel: true,
   tickPixelInterval: 50,
 }
-
+/**备注一下 越多越乱
+ * @params
+ * Source 1 ; chartType 1曲线 2季节 3面积 4堆积柱 5散点 6组合 7柱形 8截面散点
+ * 2 商品价格
+ * 3 相关性
+ * 4 滚动相关性
+ * 5 商品利润
+ * 6 拟合方程
+ * 7 统计特征/标准差
+ * 8 统计特征/百分位
+ * 9 统计特征/频率
+ * 10 跨品种分析
+ */
 export const chartSetMixin = {
 	data() {
 		return {
@@ -174,6 +186,9 @@ export const chartSetMixin = {
 				XEndDate:"12-31", // 横坐标显示范围-结束
 				JumpYear:0, //是否跨年
 			},
+
+      /* 跨品种分析图 */
+      crossVarietyChartData: {}
 		}
 	},
   watch: {
@@ -212,16 +227,17 @@ export const chartSetMixin = {
       //不同图表来源 1eta图 2商品价格图 3相关性图表
       const sourceMap = {
         1: this.changeOptions,
-        2: this.changeCommodityOptions,
-        3: this.changeRelevanceOptions,
-        4: this.changeRelevanceOptions,
-        5: this.changeCommodityOptions,
+        2: this.changeCommodityLang,
+        3: this.changeRelevanceLang,
+        4: this.changeRelevanceLang,
+        5: this.changeCommodityLang,
+        // 10: wydgw 
       }
 
       sourceMap[this.chartInfo.Source]()
 		},
     // 打开设置英文信息弹窗
-    openEnNameDia(){
+    async openEnNameDia(){
       this.formItemArray={}
       this.formItemArray.chartInfo=[]
       this.formItemArray.chartsList=[]
@@ -230,6 +246,7 @@ export const chartSetMixin = {
         value:this.chartInfo.ChartName,
         key:'ChartName',
         id:this.chartInfo.ChartInfoId,
+        source: this.chartInfo.Source,
         notEdit:true
       },
       {
@@ -239,7 +256,8 @@ export const chartSetMixin = {
         id:this.chartInfo.ChartInfoId,
         placeholder:'请输入英文图表名称'
       })
-      if(![3,6,7,8,9].includes(this.chartInfo.Source)){//相关性图表
+
+      if([1,2,5].includes(this.chartInfo.Source)){ //需要设置指标的
         this.tableData.map(item =>{
           if(item.Unit){
             this.formItemArray.chartsList.push([
@@ -312,7 +330,7 @@ export const chartSetMixin = {
       }
 
       //利润曲线
-      if(this.chartInfo.Source===5) {
+      else if(this.chartInfo.Source===5) {
         this.formItemArray.chartInfo.push({
           label:'盘面利润名称',
           value:this.chartInfo.ProfitName,
@@ -328,6 +346,62 @@ export const chartSetMixin = {
           placeholder:'请输入英文盘面利润名称'
         })
       }
+
+      //跨品种分析
+      else if(this.chartInfo.Source===10) {
+        let { Data } = await crossVarietyInterface.chartLangOption({ChartInfoId: this.chartInfo.ChartInfoId})
+
+        const { TagList,VarietyList } = Data;
+
+        this.formItemArray.chartInfo.push({
+          label:'X轴名称',
+          value:this.crossVarietyChartData.XName,
+          key:'XName',
+          id:TagList[0].ChartTagId,
+          notEdit:true
+        },
+        {
+          label:'英文X轴名称',
+          value:this.crossVarietyChartData.XNameEn,
+          key:'XNameEn',
+          id:TagList[0].ChartTagId,
+          placeholder:'请输入英文X轴名称'
+        },{
+          label:'Y轴名称',
+          value:this.crossVarietyChartData.YName,
+          key:'YName',
+          id:TagList[1].ChartTagId,
+          notEdit:true
+        },
+        {
+          label:'英文Y轴名称',
+          value:this.crossVarietyChartData.YNameEn,
+          key:'YNameEn',
+          id:TagList[1].ChartTagId,
+          placeholder:'请输入英文Y轴名称'
+        })
+
+        VarietyList.forEach(item => {
+          this.formItemArray.chartsList.push([
+            {
+              label:'品种名称',
+              value:item.ChartVarietyName,
+              key:'ChartVarietyName',
+              id:item.ChartVarietyId,
+              notEdit:true
+            },
+            {
+              label:'英文品种名称',
+              value:item.ChartVarietyNameEn,
+              key:'ChartVarietyNameEn',
+              id:item.ChartVarietyId,
+              placeholder:'请输入英文品种名称'
+            }
+          ])
+        })
+      }
+
+
       this.setEnName = true
     },
     // 更新英文信息
@@ -358,6 +432,8 @@ export const chartSetMixin = {
           ChartInfoId: enNameData.ChartInfoId,
           ChartNameEn: enNameData.ChartNameEn
         })
+      }else if(this.chartInfo.Source === 10) {//跨品种分析
+        res=await crossVarietyInterface.editChartEn(enNameData)
       }else{
         res = await dataBaseInterface.chartInfoEditEn(enNameData)
       }
@@ -431,7 +507,7 @@ export const chartSetMixin = {
     },
 
     /* 切换商品价格图中英文 */
-    changeCommodityOptions() {
+    changeCommodityLang() {
       console.log(this.options)
         this.options.yAxis.forEach(item => {
           item.title.text = this.currentLang == 'ch' ? item.title.textCh : item.title.textEn
@@ -447,8 +523,7 @@ export const chartSetMixin = {
     },
 
     /* 切换相关性图中英文 */
-    changeRelevanceOptions(){
-      console.log(this.options)
+    changeRelevanceLang(){
         this.options.yAxis.forEach(item => {
           item.title.text = this.currentLang == 'ch' ? item.title.textCh : item.title.textEn
         });
@@ -1349,7 +1424,7 @@ export const chartSetMixin = {
         tooltip
       }
 
-      this.currentLang==='en' && this.changeCommodityOptions();
+      this.currentLang==='en' && this.changeCommodityLang();
     },
 
     /* 处理无效数据为null */
@@ -1527,7 +1602,7 @@ export const chartSetMixin = {
       this.rightIndex = -1;
       this.rightTwoIndex = -1;
       
-      const { DataList,XName,XNameEn,XUnitName,XUnitNameEn,YName,YNameEn,YUnitName,YUnitNameEn } = this.sectionScatterData;
+      const { DataList,XName,XNameEn,YName,YNameEn } = this.sectionScatterData;
       const { min,max,x_min,x_max }  = this.chartLimit;
 
       //y轴
@@ -1776,6 +1851,123 @@ export const chartSetMixin = {
       this.currentLang=='en' && this.changeOptions()
     },
 
+    /* 跨品种分析 */
+    setCrossVarietyChart() {
+      this.leftIndex = -1;
+      this.rightIndex = -1;
+      this.rightTwoIndex = -1;
+      
+      const { min,max,x_min,x_max }  = this.chartLimit;
+
+      const { DataList,XName,YName,XNameEn,YNameEn } = this.crossVarietyChartData;
+
+      //y轴
+      let yAxis = {
+        ...basicYAxis,
+        title: {
+          text: YName,
+          textCh:YName,// 中文
+          textEn:YNameEn||YName,
+          align: 'middle',
+        },
+        opposite: false,
+        reversed: false,
+        min: Number(min),
+        max: Number(max),
+        tickWidth: 1,
+      }
+
+      // x轴
+      let xAxis = {
+        ...scatterXAxis,
+        title: {
+          text: XName,
+          textCh:XName,// 中文
+          textEn:XNameEn || XName,
+          align: 'middle',
+        },
+        min: Number(x_min),
+        max: Number(x_max),
+      }
+
+       //数据列
+      let series = [];
+      DataList.forEach(item => {
+        //数据列
+        let series_item = {
+          data: [],
+          type: 'scatter',
+          name: item.Name,
+          nameCh: item.Name,
+          nameEn: item.NameEn||item.Name,
+          color: item.Color,
+          lineWidth: 0,
+          chartType: 'linear',
+          zIndex:1
+        }
+        item.CoordinatePointData.forEach(_ => {
+          series_item.data.push({x: _.X,y: _.Y,})
+        })
+        series.push(series_item);
+      })
+
+      let edbInfoList = this.tableData;
+      let tooltip = {
+        formatter: function() {
+          let series_obj = DataList.find(_ => _.Name === this.series.name);
+          let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===this.x && _.Y===this.y);
+          
+          let xEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId);
+          let yEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId);
+
+          let str=`<b>${ this.series.name }</b>`;
+          str += `<br><span style="color:${this.color}">\u25CF</span>${xEdbInfo.EdbName}: ${this.x} ${ponit_obj.XDate}<br>`;
+          str += `<span style="color:${this.color}">\u25CF</span>${yEdbInfo.EdbName}: ${this.y} ${ponit_obj.YDate}`;
+
+          return str
+        },
+        formatterCh: function() {
+          let series_obj = DataList.find(_ => _.Name === this.series.name);
+          let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===this.x && _.Y===this.y);
+          
+          let xEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId);
+          let yEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId);
+
+          let str=`<b>${ this.series.name }</b>`;
+          str += `<br><span style="color:${this.color}">\u25CF</span>${xEdbInfo.EdbName}: ${this.x} ${ponit_obj.XDate}<br>`;
+          str += `<span style="color:${this.color}">\u25CF</span>${yEdbInfo.EdbName}: ${this.y} ${ponit_obj.YDate}`;
+
+          return str
+          
+        },
+        formatterEn: function() {
+          let series_obj = DataList.find(_ => _.NameEn === this.series.name);
+          let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===this.x && _.Y===this.y);
+          
+          let xEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId);
+          let yEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId);
+
+          let str=`<b>${ this.series.name }</b>`;
+          str += `<br><span style="color:${this.color}">\u25CF</span>${xEdbInfo.EdbNameEn}: ${this.x} ${ponit_obj.XDate}<br>`;
+          str += `<span style="color:${this.color}">\u25CF</span>${yEdbInfo.EdbNameEn}: ${this.y} ${ponit_obj.YDate}`;
+
+          return str
+        }
+      }
+
+      this.options = {
+        title: {
+          text:''
+        },
+        series,
+        yAxis: [yAxis],
+        xAxis,
+        tooltip
+      }
+      this.currentLang=='en' && this.changeRelevanceLang()
+
+    },
+
 		/* 查询范围为1年内 x轴显示为月/日 否则默认年/月 */
 		xLabelDealHandle() {
 			const end =
@@ -2069,12 +2261,15 @@ export const chartSetMixin = {
 
     /* 改变上下限时重绘图 依赖于chartLimit的柱 商品价格 散点截面 */
     changeLimit() {
+      //source1 eta图库的类型对应
       const typeMap = {
         7: this.setBarChart,
         10: this.setSectionScatterChart
       }
+      //其他source
       const sourceMap = {
         2: this.setCommodityChart,
+        10: this.setCrossVarietyChart
         // 3:
       }
       if(this.chartInfo.Source === 1) {

+ 564 - 0
src/views/dataEntry_manage/thirdBase/YyzxData.vue

@@ -0,0 +1,564 @@
+<template>
+  <div class="smmTarget-container target-container" id="box">
+    <span
+        v-show="!isLeftWrapShow"
+        class="slide-btn-icon slide-right"
+        @click="isLeftWrapShow = !isLeftWrapShow"
+    >
+        <i :class="{'el-icon-d-arrow-left':isLeftWrapShow,'el-icon-d-arrow-right':!isLeftWrapShow}"></i>
+    </span>
+    <div class="left-cont minHeight" id="left" v-show="isLeftWrapShow">
+        <span
+            v-show="isLeftWrapShow"
+            class="slide-btn-icon slide-left"
+            @click="isLeftWrapShow = !isLeftWrapShow"
+        >
+            <i :class="{'el-icon-d-arrow-left':isLeftWrapShow,'el-icon-d-arrow-right':!isLeftWrapShow}"></i>
+        </span>
+      <div class="left-top">
+        <el-button
+          v-permission="permissionBtn.dataSourcePermission.yyzxData_export"
+          style="width: 100%"
+          type="primary"
+          plain
+          size="medium"
+          @click="exportClick"
+          :loading="btnload"
+          :disabled="!select_classify&&!leftSearchTradeCode"
+          >导出Excel</el-button
+        >
+        <el-autocomplete
+          style="margin: 20px 0; width: 100%"
+          prefix-icon="el-icon-search"
+          v-model="leftSearchVal"
+          :fetch-suggestions="handleLeftSearch"
+          :trigger-on-focus="false"
+          placeholder="指标名称/指标ID"
+          @select="handleSelectLeftSearchval"
+          popper-class="el-autocomplete-suggestion-data-entry"
+          clearable
+        >
+          <template slot-scope="scope">
+            <div v-if="scope.item.nodata" style="text-align: center">
+              暂无数据
+            </div>
+            <div v-else>
+              {{ scope.item.IndexName }}
+            </div>
+          </template>
+        </el-autocomplete>
+      </div>
+      <div class="scroll-wrap">
+        <el-tree
+          ref="treeRef"
+          class="target_tree"
+          :data="classifyList"
+          node-key="ClassifyId"
+          :props="{
+            label: 'ClassifyName',
+            children: 'Children',
+          }"
+          :current-node-key="select_classify"
+          :expand-on-click-node="false"
+          check-strictly
+          highlight-current
+          empty-text="暂无分类"
+          @current-change="nodeChangeHandle"
+        >
+        </el-tree>
+      </div>
+      <span
+        class="move-btn resize"
+        v-drag
+        id="resize"
+        @mousemove="dynamicNode && resetNodeStyle(dynamicNode)"
+      >
+      </span>
+    </div>
+    <div
+      class="right-cont minHeight"
+      id="right"
+      v-loading="dataloading"
+      element-loading-text="获取数据中..."
+    >
+      <template v-if="rightShow">
+        <div class="right-box" @scroll="scrollHandle">
+          <div class="data-header">
+            <lz-table
+              :tableOption="tableOption"
+              tableType="header"
+              ref="table"
+              source="yyzx"
+              @editTarget="editTargetByTable"
+            />
+          </div>
+          <div class="data-cont" v-if="dateArr.length">
+            <lz-table
+              :tableOption="tableOption"
+              tableType="data"
+              :dateArr="dateArr"
+              source="yyzx"
+            />
+          </div>
+          <div v-else class="nodata"></div>
+        </div>
+      </template>
+      <div v-else class="nodata-cont">
+        <tableNoData text="暂无数据"/>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import lzTable from "@/components/lzTable.vue";
+import { yongyiInterface } from "@/api/api.js";
+export default {
+  name: "Yyzx",
+  components: { lzTable },
+  directives: {
+    drag(el, bindings) {
+      el.onmousedown = function (e) {
+        var init = e.clientX;
+        // console.log(init);
+        var box = $('#box')[0];
+        // console.log(box.clientWidth)
+        let total_wid = box.offsetWidth;
+        var left = $('#left')[0];
+        var right = $('#right')[0];
+        var initWidth = left.offsetWidth;
+        document.onmousemove = function (e) {
+          var end = e.clientX;
+          var newWidth = end - init + initWidth;
+          left.style.width = newWidth + 'px';
+          right.style.width = newWidth > 320 ? total_wid - newWidth + 'px' : total_wid - 320 + 'px';
+        };
+        document.onmouseup = function () {
+          document.onmousemove = document.onmouseup = null;
+          e.releaseCapture && e.releaseCapture();
+        };
+        e.setCapture && e.setCapture();
+        return false;
+      };
+    }
+  }, 
+  data() {
+    return {
+      isLeftWrapShow:true,
+      exportBase:process.env.VUE_APP_API_ROOT + "/datamanage/yongyi/export", //ssm数据导出接口
+      dataloading: false,
+      rightShow: false,
+      select_classify: 0,
+      classifyList: [],
+      tableOption: [],
+      dateArr: [], //最长的日期数组
+      btnload: false,
+      page_no: 1,
+      page_size: 20,
+      havemore: true, //是否还有数据
+
+      leftSearchVal: "", //左侧搜索值
+      leftSearchTradeCode: "", //如果是搜索选择的 则有此code
+      isShowSingleData: false, //右侧是否展示的是单个指标数据
+    };
+  },
+  methods: {
+    /* 获取分类 */
+    getClassify() {
+      yongyiInterface.classifyList().then((res) => {
+        if (res.Ret !== 200) return;
+        this.classifyList = res.Data.List || [];
+      });
+    },
+    /* 获取数据 */
+    getDataList: _.throttle(function () {
+      this.isShowSingleData = false;
+      this.dataloading = true;
+      yongyiInterface
+        .dataList({
+          ClassifyId: this.select_classify,
+          PageSize: this.page_size,
+          CurrentIndex: this.page_no,
+        })
+        .then((res) => {
+          this.rightShow = true;
+          if (res.Ret !== 200) return;
+
+          // 找出最多的页码 判断是否还有数据
+          let page_arrs = res.Data.map((item) => item.Paging.Pages);
+          let totalPage = Math.max.apply(Math, page_arrs);
+          this.havemore = this.page_no < totalPage ? true : false;
+
+          // 合并数据
+          if (this.page_no === 1) {
+            this.tableOption = res.Data;
+          } else {
+            this.tableOption.forEach((item) => {
+              res.Data.forEach((_item) => {
+                if (item.IndexCode === _item.IndexCode) {
+                  item.DataList = item.DataList.concat(_item.DataList);
+                }
+              });
+            });
+          }
+
+          // 合并所有指标中的日期 作为日期数组
+          let arr = res.Data.map((item) => {
+            return item.DataList;
+          });
+
+          let obj = [];
+          for (let i of arr) {
+            for (let j of i) {
+              obj.push(j.DataTime);
+            }
+          }
+          let arr2 = [...new Set(obj)].sort().reverse();
+          let concatArr = [...new Set([...this.dateArr, ...arr2])]
+            .sort()
+            .reverse();
+          this.dateArr = this.page_no === 1 ? arr2 : concatArr;
+
+          /* 不满6个追加6个空的显示一排 别问 问就是为了美观  */
+          if (this.tableOption.length < 7)
+            for (let i = 0; i < 7; i++) {
+              this.tableOption.push({
+                DataList: [],
+              });
+              if (this.tableOption.length >= 7) break;
+            }
+
+          //数据最大长度小于12个 追加数据满12个 别问 问就是为了美观
+          if (this.dateArr.length < 12)
+            for (let i = 0; i < 12; i++) {
+              this.dateArr.push("");
+              if (this.dateArr.length >= 12) break;
+            }
+
+          this.dataloading = false;
+          this.page_no === 1 &&
+            this.$nextTick(() => {
+              this.rightShow && this.initWidth();
+            });
+        });
+    }, 200),
+    // 获取单个指标数据
+    async getTargetDataList(code) {
+      this.isShowSingleData = true;
+      this.dataloading = true;
+      try {
+        const res = await yongyiInterface.getTargetDataList({
+          IndexCode: code,
+        });
+        this.rightShow = true;
+        if (res.Ret !== 200) return;
+        const DataList = res.Data.Data || [];
+        // 设置为没有更多数据
+        this.haveMore = false;
+        // 合并数据
+        this.tableOption = [
+          {
+            DataList: DataList,
+            ...res.Data,
+          },
+        ];
+        // 这里是单个指标所以不用合并日期
+        const arr = DataList.map((item) => item.DataTime);
+        this.dateArr = [...new Set(arr)].sort().reverse();
+        /* 不满6个追加6个空的显示一排 别问 问就是为了美观  */
+        for (let i = 0; i < 7; i++) {
+          this.tableOption.push({
+            DataList: [],
+          });
+          if (this.tableOption.length >= 7) break;
+        }
+        //数据最大长度小于12个 追加数据满12个 别问 问就是为了美观
+        if (this.dateArr.length < 12)
+          for (let i = 0; i < 12; i++) {
+            this.dateArr.push("");
+            if (this.dateArr.length >= 12) break;
+          }
+        this.select_quota = res.Data.IndexName;
+        this.select_Unit = res.Data.Unit;
+        this.select_frequency = res.Data.Frequency;
+        this.select_ModifyTime = res.Data.ModifyTime || "";
+        this.dataloading = false;
+        this.rightShow && this.initWidth();
+      } catch (err) {
+        console.log(err);
+      }
+    },
+    initWidth() {
+      this.$nextTick(() => {
+        $(".right-box")[0].style.width =
+          this.$refs.table.$el.clientWidth + 5 + "px";
+        $(".right-box")[0].scrollTop = 0;
+        $(".right-box")[0].scrollLeft = 0;
+      });
+    },
+    /* 无频度的异常显示处理 7*12*/
+    nodataDeal() {
+      this.tableOption = [];
+      this.dateArr = [];
+      for (let i = 0; i < 7; i++) {
+        this.tableOption.push({
+          DataList: [],
+        });
+        if (this.tableOption.length >= 7) break;
+      }
+      for (let i = 0; i < 12; i++) {
+        this.dateArr.push("");
+        if (this.dateArr.length >= 12) break;
+      }
+    },
+    /* 滚动加载 */
+    scrollHandle(e) {
+      if (this.isShowSingleData) return;
+      const dom = e.target;
+      let total = dom.scrollTop + dom.clientHeight;
+      if (total >= dom.scrollHeight && this.havemore) {
+        this.page_no++;
+        console.log("load下一页");
+        this.getDataList();
+      }
+    },
+    /* 数据导出 */
+    exportClick() {
+      this.btnload = true;
+      const link = document.createElement("a");
+      link.href = this.exportDataUrl;
+      link.download = "";
+      link.click();
+      setTimeout(() => {
+        this.btnload = false;
+      }, 5000);
+    },
+    //左侧搜索
+    async handleLeftSearch(query, cb) {
+      cb([]);
+      if (!query) return;
+      const res = await yongyiInterface.getTargetListByName({
+        Keyword: query,
+      });
+      if (res.Ret === 200) {
+        let arr = res.Data || [];
+        if (!arr.length) {
+          cb([{ nodata: true }]);
+        } else {
+          cb(arr);
+        }
+      }
+    },
+    // 选中左侧搜索值
+    handleSelectLeftSearchval(e) {
+      if (!e.IndexCode) return;
+      this.leftSearchTradeCode = e.IndexCode;
+      this.leftSearchVal = e.IndexName;
+
+      // 获取单独指标数据
+      this.getTargetDataList(e.IndexCode);
+    },
+
+    getTargetData(code, arr) {
+      for (const item of arr) {
+        if (item.BaseFromBaiinfoIndexCode === code) return item;
+        if (item.Children && item.Children.length) {
+          const _item = this.getTargetData(code, item.Children);
+          if (_item) return _item;
+        }
+      }
+    },
+    // 对[#,;]转义
+    escapeStr(str) {
+      return str.replace(/#/g, escape("#")).replace(/;/g, escape(";")); 
+    },
+  
+    //改变选中节点
+    nodeChangeHandle(data, node) {
+      if (data.ClassifyId === this.select_classify) return;
+      this.select_classify = data.ClassifyId;
+      this.leftSearchVal=''
+      this.page_no = 1;
+      this.page_size = 20;
+      this.getDataList()
+    },
+   
+  },
+  computed: {
+    exportDataUrl() {
+      // 数据导出接口
+      let urlStr = this.exportBase;
+      // token
+      urlStr += `?${localStorage.getItem("auth") || ""}`;
+      if (this.isShowSingleData) {
+        // 指标id
+        urlStr += `&IndexCode=${
+          this.isShowSingleData ? this.leftSearchTradeCode : ""
+        }`;
+      } else {
+        // 目录id
+        urlStr += `&ClassifyId=${
+          this.isShowSingleData ? "" : this.select_classify
+        }`;
+      }
+      return this.escapeStr(urlStr);
+    },
+  },
+  created() {},
+  mounted() {
+    this.getClassify();
+  },
+};
+</script>
+<style lang="scss">
+@import "../css/customtree.scss";
+@import "../css/baseTargetPage.scss";
+.smmTarget-dialog-cont {
+  .el-cascader {
+    .el-input {
+      width: 100% !important;
+    }
+  }
+}
+</style>
+<style lang="scss" scoped>
+.smmTarget-container {
+  display: flex;
+  * {
+    box-sizing: border-box;
+  }
+  .minHeight {
+    height: calc(100vh - 120px);
+    background-color: #fff;
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.05);
+    border-radius: 4px;
+  }
+  div::-webkit-scrollbar {
+    width: 5px !important;
+  }
+  .left-cont {
+    min-width: 300px;
+    width: 300px;
+    margin-right: 20px;
+    padding: 30px 0;
+    overflow: hidden;
+    position: relative;
+    .left-top {
+      padding: 0 20px;
+    }
+    .scroll-wrap {
+      padding: 0 10px;
+      height: calc(100vh - 280px);
+      overflow-y: auto;
+      .target_tree {
+        color: #333;
+        .label-input .el-input__inner {
+          height: 25px;
+          line-height: 25px;
+        }
+        .custom-tree-node {
+          display: flex !important;
+          justify-content: space-between;
+          align-items: center;
+          display: block;
+          flex: 1;
+          .node_label {
+            margin-right: 2px;
+          }
+          .el-icon-view {
+            color: #409eff;
+            font-size: 18px;
+            margin-left: 5px;
+          }
+        }
+      }
+      .add-cont {
+        margin: 50px 0 20px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: #409eff;
+        font-size: 16px;
+        cursor: pointer;
+      }
+    }
+    .move-btn {
+      height: 100%;
+      width: 4px;
+      position: absolute;
+      right: 0px;
+      top: 0;
+      &:hover {
+        cursor: col-resize;
+      }
+    }
+    .classify-list {
+      padding: 0 20px;
+      /* margin-top: 20px; */
+      height: calc(100vh - 280px);
+      overflow-y: auto;
+      .classify-item {
+        font-size: 14px;
+        color: #666;
+        margin-bottom: 20px;
+        &:hover {
+          cursor: pointer;
+          color: #409eff;
+        }
+        &.act {
+          color: #409eff;
+        }
+      }
+    }
+  }
+  .right-cont {
+    width: 82%;
+    padding: 30px;
+    .right-box {
+      max-width: 100%;
+      max-height: calc(100vh - 230px);
+      border-left: 1px solid #dcdfe6;
+      border-right: 1px solid #dcdfe6;
+      overflow: auto;
+      .data-header {
+        width: 100%;
+        position: sticky;
+        top: 0;
+        z-index: 2;
+      }
+      .data-cont {
+        height: calc(100vh - 444px);
+      }
+      .nodata {
+        height: calc(100vh - 460px);
+        border: 1px solid #dcdfe6;
+        font-size: 16px;
+        color: #999;
+      }
+    }
+    .frequency-list {
+      margin-top: 20px;
+      display: flex;
+      flex-wrap: wrap;
+      .frequency-btn {
+        width: 112px;
+        margin: 0 30px 10px 0;
+      }
+    }
+    .nodata-cont {
+      width: 150px !important;
+      text-align: center;
+      color: #666;
+      font-size: 16px;
+      margin: 0 auto;
+    }
+  }
+}
+.dialog-cont {
+  padding-left: 50px;
+}
+.dia-bot {
+  display: flex;
+  justify-content: center;
+}
+</style>

+ 795 - 0
src/views/dataEntry_manage/thirdBase/ysTargetBase.vue

@@ -0,0 +1,795 @@
+<template>
+  <div id="YS-data-container" v-loading="dataLoading" element-loading-text="指标数据加载中,请勿切换到其他页面">
+    <div class="search-zone">
+      <div class="search-row">
+        <el-cascader @change="searchList" :options="classifyList" v-model="typesList" clearable
+          placeholder="请选择分类" class="search-item"
+          :props="{
+            value:'Type',
+            label:'Type',
+            children:'Child'
+          }"></el-cascader>
+        <el-select v-model="searchParams.Frequency" class="search-item" placeholder="请选择频度" clearable @change="searchList">
+          <el-option :label="item" :value="item" v-for="item in frequencyList" :key="item"></el-option>  
+        </el-select>
+        <el-select v-model="searchParams.DataState" class="search-item" placeholder="请选择指标数据状态" clearable @change="searchList">
+          <el-option :label="item.label" :value="item.value" v-for="item in dataStatusList" :key="item.value"></el-option>  
+        </el-select>
+        <el-select v-model="edbCheckList" multiple filterable remote clearable collapse-tags class="search-item edb-filter"
+          reserve-keyword placeholder="指标ID/指标名称" :remote-method="remoteMethod" 
+          v-loadMore="searchLoad" @clear="searchList" @remove-tag="removeEdbFilterTag" ref="edbFilterRef" >
+          <el-option v-for="item in edbFilterDataList" :key="item.IndexCode"
+            :label="item.IndexName" :value="item.IndexCode">
+          </el-option>
+        </el-select>
+        <el-checkbox label="列表全选" v-model="isCheckAll" :indeterminate="isCheckIndeterminate" style="margin: 0 20px 10px 0;"
+        @change="listCheckAllChange"></el-checkbox>
+      </div>
+      <el-button type="primary" style="margin: 0 0 10px 0;padding:12px 18px ;" @click="addToEdbBase" 
+      :disabled="addBaseDisable">添加到指标库</el-button>
+    </div>
+    <div class="table-zone">
+      <el-table :data="tableData" border @sort-change="sortChange"  @selection-change="selectionChange"
+      ref="edbDataRef" @select="selectHandle" @select-all="selectAllHandle">
+        <el-table-column type="selection" min-width="50" align="center"></el-table-column>
+        <el-table-column label="指标ID" align="center" prop="IndexCode">
+          <template slot-scope="{row}">
+            {{row.IndexCode}}
+          </template>
+        </el-table-column>
+        <el-table-column label="指标名称" align="center" prop="IndexName">
+          <template slot-scope="{row}">
+            {{row.IndexName}}
+          </template>
+        </el-table-column>
+        <el-table-column label="频度" align="center" prop="Frequency" width="80">
+          <template slot-scope="{row}">
+            {{row.Frequency}}
+          </template>
+        </el-table-column>
+        <el-table-column label="单位" align="center" prop="Unit" >
+          <template slot-scope="{row}">
+            {{row.Unit}}
+          </template>
+        </el-table-column>
+        <el-table-column label="指标开始时间" align="center" prop="StartDate"
+        sortable="custom">
+          <template slot-scope="{row}">
+            {{row.StartDate}}
+          </template>
+        </el-table-column>
+        <el-table-column label="指标最新时间" align="center" prop="EndDate"
+        sortable="custom" >
+          <template slot-scope="{row}">
+            {{row.EndDate}}
+          </template>
+        </el-table-column>
+        <el-table-column label="更新时间" align="center" prop="ModifyTime" sortable="custom">
+          <template slot-scope="{row}">
+            {{row.ModifyTime}}
+          </template>
+        </el-table-column>
+        <el-table-column label="预计发布时间" align="center" prop="ReleaseTime" sortable="custom" >
+          <template slot-scope="{row}">
+            {{row.ReleaseTime}}
+          </template>
+        </el-table-column>
+        <el-table-column label="指标数据状态" align="center" prop="DataState" >
+          <template slot-scope="{row}">
+            {{row.DataState?
+            dataStatusList.find(it => it.value == row.DataState)?dataStatusList.find(it => it.value == row.DataState).label:''
+            :'正常更新'}}
+          </template>
+        </el-table-column>
+        <el-table-column label="分类" align="center" prop="TypeAll" >
+          <template slot-scope="{row}">
+            {{row.TypeAll}}
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <m-page class="table-page" v-show="total"
+    :total="total" :pageSize="searchParams.PageSize"
+    :page_no="searchParams.CurrentIndex"
+    @handleCurrentChange="pageNumberChange"/>
+    <!-- 指标重复弹窗 -->
+    <el-dialog :visible.sync="checkFailShow" :close-on-click-modal="false" :modal-append-to-body='false' 
+			width="600px" title="操作提示">
+			<div class="check-fail-box">
+				<div>
+					<div style="margin-bottom: 20px;">
+            {{ indexAllExist?'本次添加的指标均已在指标库中,请勿重复添加!':'指标库中已存在以下指标,会自动过滤!' }}
+          </div>
+					<div v-for="(item,index) in existIndexList" class="exist-index-item" @click="existIndexClick(item)">
+						{{ index+1+'、'+item.text }}
+					</div>
+				</div>
+				<div class="check-fail-button">
+					<el-button type="primary" @click="edbCheckFinished" style="width: 120px;">知道了</el-button>
+				</div>
+			</div>
+		</el-dialog>
+    <!-- 添加到指标库 -->
+    <el-dialog :visible.sync="addToEbdDiaShow" :close-on-click-modal="false" :modal-append-to-body='false' 
+    width="980px" title="添加到指标库">
+      <div class="edb-table-box">
+        <div class="edb-table-preview">
+          <table width="auto" border="0">
+            <thead>
+              <tr v-for="(item, index) in edbTableHeadKey" :key="item">
+                <td class="sticky" style="left: 0;text-align: center;">
+                  {{ edbTableHeadData.get(item) }}
+                </td>
+                <td v-for="(data, sub_index) in edbIndexDatas" :key="sub_index" :colspan="2" 
+                :class="highLightIndex.includes(data.EdbName) && item=='EdbName' ?'exist-highlight':''">
+                  <template v-if="item === 'ClassifyId'">
+                    <el-cascader :options="options" v-model="data[item]" placeholder="请选择所属目录"
+                    size="mini" :disabled="!data.Source"
+                    :props="{label: 'ClassifyName',
+                      value: 'ClassifyId',
+                      children: 'Children',
+                      checkStrictly: true,
+                      emitPath:false}">
+                    </el-cascader>
+                  </template>
+                  <template v-else-if="item === 'Unit'">
+                    <el-autocomplete
+                    :disabled="!data.Source"
+                      v-model.trim="data[item]"
+                      :fetch-suggestions="querySearchUnit"
+                      placeholder="请输入单位"
+                      suffix-icon="el-icon-arrow-down"
+                      size="mini"
+                    ></el-autocomplete>
+                  </template>
+                  <template v-else-if="item === 'Frequency'">
+                    <el-select v-model="data[item]" placeholder="请选择频度" size="mini" :disabled="!data.Source">
+                      <el-option :label="item" :value="item"
+                      v-for="item in edbFrequencyList" :key="item"></el-option>
+                    </el-select>
+                  </template>
+                  <template v-else-if="item === 'EdbName'">
+                    <el-input v-model.trim="data[item]" placeholder="请输入指标名称" size="mini" :disabled="!data.Source"></el-input>
+                  </template>
+                  <template v-else>
+                    <div style="padding: 0 7px;">{{ data[item] }}</div>
+                  </template>
+                </td>
+              </tr>
+            </thead>
+            <tbody v-if="edbIndexDatas && edbIndexDatas.length>0">
+              <tr v-for="(item,index) in edbIndexDatas[0].DataList.length" :key="index">
+                <td :rowspan="edbIndexDatas[0].DataList.length" v-if="index==0"
+                class="sticky" style="left: 0;text-align: center;">
+                  数据详情
+                </td>
+                <template v-for="(item1,index1) in edbIndexDatas.length">
+                  <td>{{ edbIndexDatas[index1].DataList[index].DataTime }}</td>
+                  <td>{{ edbIndexDatas[index1].DataList[index].Value }}</td>
+                </template>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+        <div class="dia-bot">
+          <el-button @click="addToEbdDiaShow=false" style="width: 120px;">取消</el-button>
+          <el-button type="primary" @click="ysEdbSaveHandle" style="width: 120px;margin-left: 30px;" :loading="saveLoading">保存</el-button>
+        </div>
+      </div>
+		</el-dialog>
+  </div>
+</template>
+
+<script>
+import mPage from '@/components/mPage.vue'
+import { dataBaseInterface,dataInterence,smmDataInterface} from '@/api/api.js'
+import { frequencyArr} from '../databaseComponents/util';
+
+  export default {
+    name:'ysTargetBase',
+    components:{mPage},
+    data() {
+      return {
+        searchParams:{
+          Types:'',
+          Frequency:'',
+          DataState:'',
+          CurrentIndex:1,
+          PageSize:10,
+          SortParam:'',
+          SortType:'',
+          IndexCodes:''
+        },
+        typesList:[],
+        options:[],
+        isCheckAll:false,
+        isCheckIndeterminate:false,
+        // 标志列表当前是全选状态还是不是全选状态和 isCheckAll不一样
+        checkAllStatus:false,
+        classifyList:[],
+        frequencyList: ["日度", "周度", "月度", "季度", "半年", "年度"],
+        edbFrequencyList:frequencyArr,
+        dataStatusList:[{value:'normal',label:'正常更新'},{value:'ceased',label:'已停更'},{value:'irregular',label:'更新依赖外部会有不定期延迟'}],
+        forSearchParams:{
+          Keyword:'',
+          CurrentIndex:1,
+          PageSize:20,
+        },
+        searchHaveMore: false,
+        edbFilterDataList:[],
+        // 选择的指标名称筛选项
+        edbCheckList:[],
+        // edb的过滤选项是否有变化,用于失焦是判断是否需要请求
+        hasEdbFilterChange:false,
+        selectionReactCancel:false, // 是否不触发 selection的逻辑
+        tableData:[],
+        tableDataIds:[],
+        total:0,
+        dataLoading:false,
+        tableDataCheckedList:[],
+        // 添加到指标库
+        existIndexList:[],
+        checkFailShow:false,
+        indexAllExist:false,//指标是否都存在
+        edbIndexDatas:[],
+        edbTableHeadKey: [
+          "ClassifyId",
+          "Unit",
+          "Frequency",
+          "EdbName",
+          "EdbCode",
+        ],
+        edbTableHeadData:new Map([
+          ["ClassifyId", "所属目录"],
+          ["Unit", "单位"],
+          ["Frequency", "频度"],
+          ["EdbName", "指标名称"],
+          ["EdbCode", "指标ID"],
+        ]),
+        BatchList:[],
+        addToEbdDiaShow:false,
+        highLightIndex:[],
+        saveLoading:false
+      }
+    },
+    computed:{
+      addBaseDisable(){
+        return !(this.isCheckAll || this.isCheckIndeterminate ) || (!(this.tableData && this.tableData.length>0))
+      }
+    },
+    watch:{
+      edbCheckList(value){
+        this.hasEdbFilterChange=true
+      },
+      typesList(value){
+        if(value && value.length>0){
+          this.searchParams.Types = value.join(',')
+        }else{
+          this.searchParams.Types=''
+        }
+      }
+    },
+    created() {
+      this.getList()
+      this.getYsClassifyList()
+    },
+    mounted(){
+      // el-select 的@blur绑定事件不生效代替方法
+      this.$refs.edbFilterRef.$refs.input.blur=()=>{
+        if(this.hasEdbFilterChange){
+          this.searchList()
+          this.hasEdbFilterChange=false
+        }
+      }
+    },
+    methods: {
+      //获取有色数据库数据列表
+      getList(type){
+        if(this.edbCheckList && this.edbCheckList.length>0){
+          this.searchParams.IndexCodes = this.edbCheckList.join(',')
+        }else{
+          this.searchParams.IndexCodes = ''
+        }
+        smmDataInterface.getYsDataList(this.searchParams).then(res=>{
+          if(res.Ret == 200){
+            this.tableData=res.Data.List || []
+            this.total = res.Data.Paging.Totals || 0
+            if(this.tableData.length>0){
+              this.tableDataIds = this.tableData.map(it => it.IndexCode)
+            }else{
+              this.tableDataIds = []
+            }
+            if(type == 'adjustSelection'){
+              this.adjustSelection()
+            }else{
+              if(this.searchParams.Types || this.searchParams.Frequency || this.searchParams.DataState || this.searchParams.IndexCodes){
+                this.isCheckAll=true
+                this.checkAllStatus=true
+                this.isCheckIndeterminate=false
+              }else{
+                this.isCheckAll=false
+                this.checkAllStatus=false
+                this.isCheckIndeterminate=false
+              }
+              this.listCheckAllChange(this.isCheckAll)
+            }
+          }
+        })
+      },
+      // 获取有色数据库分类列表
+      getYsClassifyList(){
+        smmDataInterface.getYsTypeDataList().then(res=>{
+          if(res.Ret == 200){
+            this.classifyList=res.Data || []
+          }
+        })
+
+      },
+      // 指标名称的远程搜索方法
+      remoteMethod(text){
+        this.forSearchParams.Keyword = text.trim()
+        this.forSearchParams.CurrentIndex = 1;
+        if(this.forSearchParams.Keyword){
+          this.searchApi()
+        }else{
+          this.edbFilterDataList = [];
+        }
+      },
+      searchApi(){
+        smmDataInterface.getYsDataList(this.forSearchParams).then(res=>{
+          if(res.Ret == 200){
+            let arr=res.Data.List || []
+            let {Paging}=res.Data
+            this.searchHaveMore = this.forSearchParams.CurrentIndex < Paging.Pages;
+            this.edbFilterDataList = this.forSearchParams.CurrentIndex === 1 ? arr : this.edbFilterDataList.concat(arr);
+          }
+        })
+      },
+      searchLoad(){
+        if(!this.searchHaveMore) return;
+        this.forSearchParams.CurrentIndex++
+			  this.searchApi();
+      },
+	    removeEdbFilterTag: _.throttle(function() {
+        this.searchList()
+		  },1000),
+      searchList(type){
+        this.searchParams.CurrentIndex=1
+        this.getList(type)
+      },
+      pageNumberChange(page_no){
+        this.searchParams.CurrentIndex=page_no
+        this.getList('adjustSelection')
+      },
+      sortChange({prop,order}){
+        this.searchParams.SortType = order?order==='ascending'?'asc':'desc':''
+        this.searchParams.SortParam = order?prop:''
+        this.searchList('adjustSelection')
+      },
+      selectionChange(val){
+        if(this.selectionReactCancel) return 
+        // selectAllHandle的触发在selectionChange后面,将selectionChange的逻辑延迟一下
+        setTimeout(()=>{
+          // 去重
+          let duplicateArr = Array.from(new Set(this.tableDataCheckedList))
+          
+          if((duplicateArr.length == this.total && (!this.checkAllStatus))
+          || (duplicateArr.length == 0 && this.checkAllStatus)){
+            this.isCheckAll = true
+            this.isCheckIndeterminate=false
+          }else if((duplicateArr.length == 0 && (!this.checkAllStatus))
+          || (duplicateArr.length == this.total && this.checkAllStatus)){
+            this.isCheckAll = false
+            this.isCheckIndeterminate=false
+          }else{
+            this.isCheckAll = false
+            this.isCheckIndeterminate=true
+          }
+        },1)
+
+      },
+      selectHandle(selection,row){
+        if(this.selectionReactCancel) return 
+
+        let check = false; // 从tableDataCheckedList 添加还是删除
+        if(selection.some(it => it.IndexCode == row.IndexCode)){
+          // 勾选
+          if(this.checkAllStatus){
+            check=false
+          }else{
+            check=true
+          }
+        }else{
+          // 取消勾选
+          if(this.checkAllStatus){
+            check=true
+          }else{
+            check=false
+          }
+        }
+        if(check){
+          this.tableDataCheckedList.push(row.IndexCode)
+        }else{
+          this.tableDataCheckedList=this.tableDataCheckedList.filter(it => it!=row.IndexCode)
+        }
+      },
+      selectAllHandle(selection){
+        if(this.selectionReactCancel) return 
+
+        let check = false; // 从tableDataCheckedList 添加还是删除
+        if(selection && selection.length>0){
+          // 全选
+          if(this.checkAllStatus){
+            check=false
+          }else{
+            check=true
+          }
+        }else{
+          // 全不选
+          if(this.checkAllStatus){
+            check=true
+          }else{
+            check=false
+          }
+        }
+        if(check){
+          this.tableDataCheckedList =  [...this.tableDataCheckedList,...this.tableDataIds]
+        }else{
+          this.tableDataCheckedList = this.tableDataCheckedList.filter(it => !this.tableDataIds.includes(it))
+        }
+      },
+      listCheckAllChange(value){
+        this.tableDataCheckedList=[]
+        this.checkAllStatus=value
+        if(value){
+          // 全选
+          this.$refs.edbDataRef && this.$refs.edbDataRef.clearSelection()
+          this.$refs.edbDataRef && this.$refs.edbDataRef.toggleAllSelection()
+        }else{
+          //全不选
+          this.$refs.edbDataRef && this.$refs.edbDataRef.clearSelection()
+        }
+      },
+      adjustSelection(){
+        this.selectionReactCancel=true
+        if(!this.checkAllStatus){
+          this.tableDataCheckedList.map(it =>{
+            let row = this.tableData.find(da => da.IndexCode==it)
+            if(row){
+              setTimeout(()=>{
+                this.$refs.edbDataRef.toggleRowSelection(row,true)
+              },10)
+            }
+          })
+        }else{
+          this.$refs.edbDataRef.toggleAllSelection()
+          this.tableDataCheckedList.map(it =>{
+            let row = this.tableData.find(da => da.IndexCode==it)
+            if(row){
+              setTimeout(()=>{
+                this.$refs.edbDataRef.toggleRowSelection(row,false)
+              },50)
+            }
+          })
+        }
+        setTimeout(()=>{
+          this.selectionReactCancel=false
+        },50)
+      },
+      // 添加指标库
+      addToEdbBase:_.throttle(function() {
+        if(this.dataLoading || this.addBaseDisable) return 
+        let params={
+          Types:this.searchParams.Types,
+          Frequency:this.searchParams.Frequency,
+          DataState:this.searchParams.DataState,
+          SelectAll:this.checkAllStatus,
+          SelectCode:this.searchParams.IndexCodes,
+          EdbCode:this.tableDataCheckedList.join(',')
+        }
+        // return 
+        smmDataInterface.getYsEdbCodeCheck(params).then(res=>{
+          if(res.Ret == 200){
+            // console.log(res,'res');
+            if(res.Data.IndexExist){
+              // 有重复
+              this.existIndexList=[]
+              let existEdbInfo=res.Data.ExistEdbInfo || []
+              let text=''
+              existEdbInfo.map(item =>{
+                text=`${item.EdbName}(${item.EdbCode})`
+                this.existIndexList.push({text,code:item.UniqueCode,id:item.EdbInfoId,Types:item.ClassifyId,edbCode:item.EdbCode})
+              })
+              this.checkFailShow=true
+              this.indexAllExist=res.Data.ExistAll+'' =='false' ? false :res.Data.ExistAll || true
+            }else{
+              // 没有重复
+              this.getTargetData()
+            }
+          }
+        })
+      },300),
+      edbCheckFinished(){
+        if(!this.indexAllExist){
+          this.getTargetData()
+        }
+        this.checkFailShow=false
+      },
+      getTargetData(){
+        this.dataLoading=true
+        this.getMenu()
+        this.getTargetUnitList()
+        let existEdbList = this.existIndexList.map(item => item.edbCode)
+        let afterFilterList=[]
+        if(this.checkAllStatus){
+          afterFilterList = Array.from(new Set([...this.tableDataCheckedList,...existEdbList]))
+        }else{
+          afterFilterList= this.tableDataCheckedList.filter(item => !existEdbList.includes(item))
+        }
+
+        // return 
+        let params={
+          Types:this.searchParams.Types,
+          Frequency:this.searchParams.Frequency,
+          DataState:this.searchParams.DataState,
+          SelectAll:this.checkAllStatus,
+          SelectCode:this.searchParams.IndexCodes,
+          EdbCode:afterFilterList.join(',')
+        }
+        smmDataInterface.getYsEdbCodeSearch(params).then(res => {
+          if(res.Ret == 200){
+            let stockList = res.Data.StockSearchList || []
+            this.edbIndexDatas=[]
+            this.BatchList=[]
+            stockList.map((item,index) =>{
+              let params={
+                Source:11,
+                ClassifyId:0,
+                Unit:item.Unit,
+                Frequency:this.edbFrequencyList.includes(item.Frequency)?item.Frequency:'',
+                EdbName:item.EdbName,
+                EdbCode:item.EdbCode,
+              }
+              this.BatchList.push(params)
+              let datas = item.DataList || []
+              let datasLength = datas.length
+              if( datasLength<10){
+                for (let i = datasLength; i < 10; i++) {
+                  datas.push({DataTime:'',Value:''})													
+                }
+              }
+              this.edbIndexDatas.push({...params,DataList:datas})
+            })
+
+            let edbIndexDataLength = this.edbIndexDatas.length
+            //填充空的列
+            for (let i = edbIndexDataLength; i < 4; i++) {
+              this.edbIndexDatas.push({
+                Source:0,
+                ClassifyId:0,
+                Unit:'',
+                Frequency:'',
+                EdbName:'',
+                EdbCode:'',
+                DataList:new Array(10).fill({DataTime:'',Value:''}),
+              })
+            }
+            this.addToEbdDiaShow=true
+          }
+        }).finally(()=>{
+          this.dataLoading=false
+        })
+      },
+      /* 获取目录结构 */
+      getMenu() {
+        dataBaseInterface.menuListV3().then(res => {
+          if(res.Ret === 200) {
+            this.options = res.Data.AllNodes || [];
+            this.filterNodes(this.options);
+          }
+        })
+      },
+      // 递归改变目录结构
+      filterNodes(arr) {
+        arr.length && arr.forEach(item => {
+          item.Children.length && this.filterNodes(item.Children)
+          if(!item.Children.length) {
+            item.Children=null
+          }
+        })
+      },
+      // 获取指标单位
+      async getTargetUnitList(){
+        let res=await dataInterence.getTargetUnitList()
+        if(res.Ret===200){
+          this.unitList=res.Data&&res.Data.map(item=>{
+            return {value:item}
+          })
+        }
+      },
+      //搜索单位
+      querySearchUnit(queryString, cb){
+        let results = queryString ? this.unitList.filter(item=>item.value.indexOf(queryString) === 0) : this.unitList;
+        // 调用 callback 返回建议列表的数据
+        cb(results);
+      },
+      existIndexClick({code,id,classifyId}){
+        const { href } = this.$router.resolve({ path: '/database',query:{code,id,classifyId}});
+        window.open(href, '_blank');
+      },
+      ysEdbSaveHandle(){
+        let flag = this.edbIndexDatas.some(it => {
+				  return it.Source && ((!it.ClassifyId) || (!it.Unit) || (!it.Frequency) || (!it.EdbName) || (!it.EdbCode))
+        })
+        if(flag){
+          this.$message.warning("指标信息未填写完整")
+          return 
+        }
+        this.BatchList.map((it,ind) => {
+          it.ClassifyId = this.edbIndexDatas[ind].ClassifyId
+          it.EdbCode = this.edbIndexDatas[ind].EdbCode
+          it.EdbName = this.edbIndexDatas[ind].EdbName
+          it.Frequency = this.edbIndexDatas[ind].Frequency
+          it.Unit = this.edbIndexDatas[ind].Unit
+        })
+        this.saveLoading=true
+        // console.log({BatchList:this.BatchList});
+        // return 
+        smmDataInterface.ysEdbAddBatch({BatchList:this.BatchList}).then(res=>{
+          if(res.Ret == 200){
+            this.$message.success("添加指标成功")
+            let {href} = this.$router.resolve({
+              path:'/database',
+              query:{code:res.Data.UniqueCode,id:res.Data.EdbInfoId,classifyId:res.Data.ClassifyId}
+            });
+            window.open(href,'_blank');
+            this.addToEbdDiaShow=false	
+          }else if(res.Ret == 403){
+            this.highLightIndex=res.Data?res.Data.ExistEdbName || []:[]
+          }
+        }).finally(()=>{
+          this.saveLoading=false
+        })
+      }
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  #YS-data-container{
+    // min-width: 1360px;
+    position: relative;
+    box-sizing: border-box;
+    .search-zone{
+      background-color: white;
+      display: flex;
+      align-items: flex-start;
+      justify-content: space-between;
+      padding: 20px 20px 10px;
+      border: 1px solid #C8CDD9;
+      margin-bottom: 20px;
+      .search-row{
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        .search-item{
+          width: 240px;
+          margin: 0 20px 10px 0;
+        }
+      }
+
+    }
+    .table-zone{
+      padding: 20px 20px 70px;
+      background-color: white;
+      border: 1px solid #C8CDD9;
+      min-height: calc(100vh - 215px);
+      box-sizing: border-box;
+    }
+    .table-page{
+      position: absolute;
+      bottom: 20px;
+      right: 20px;
+    }
+    .edb-table-box{
+      padding: 0 40px;
+      .edb-table-preview {
+        overflow: auto;
+        height: calc(100% - 124px);
+        &::-webkit-scrollbar {
+          width: 5px !important;
+        }
+        table {
+          border-color: #dcdfe6;
+          border-bottom: 1px solid #dcdfe6;
+          border-right: 1px solid #dcdfe6;
+          border-collapse: separate;
+        }
+        thead {
+          position: sticky;
+          z-index: 2;
+          top: 0;
+        }
+        td {
+          min-width: 100px;
+          max-width: 100px;
+          height: 30px;
+          // text-align: center;
+          color: #333;
+          box-sizing: border-box;
+          padding: 0 5px;
+          background-color: #fff;
+          border-top: 1px solid #dcdfe6;
+          border-left: 1px solid #dcdfe6;
+        }
+        .exist-highlight{
+          border: red solid 1px;
+        }
+      }
+      .dia-bot {
+        margin: 30px 0;
+        display: flex;
+        justify-content: center;
+      }
+    }
+
+    .check-fail-box{
+      padding: 0 0 15px 40px;	
+      color: #333333;
+      .exist-index-item{
+        cursor: pointer;
+        &:hover{
+          text-decoration: underline;
+        }
+      }
+      .check-fail-button{
+        margin-top: 80px;
+        display: flex;
+        justify-content: flex-end;
+      }
+    }
+  }
+</style>
+<style lang="scss">
+  #YS-data-container{
+    .el-select{
+      .el-tag{
+        max-width: 100%;
+        display: inline-flex;
+        align-items: center;
+        .el-select__tags-text{
+          flex-grow: 1;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+        .el-tag__close{
+          min-width: 16px;
+        }
+      }
+      .el-select__input{
+        min-width: 80px;
+      }
+    }
+    .edb-table-preview{
+      border: 1px solid #dcdfe6;
+      .el-cascader{
+        width: 100%;
+      }
+      .el-autocomplete{
+        width: 100%;
+      }
+      .el-select{
+        width: 100%;
+      }
+      .el-input{
+        width: 100%;
+        input{
+          border: none;
+          padding-left: 7px;
+          font-size: 14px;
+        }
+      }
+    }
+  }
+
+</style>

+ 2 - 2
src/views/datasheet_manage/components/SheetExcel.vue

@@ -56,12 +56,12 @@ export default {
     copyData() {
       let rangeArr = luckysheet.getRangeArray('twoDimensional');
       let str = ''
-      rangeArr.forEach(item => {
+      rangeArr.forEach((item,r_index) => {
         let row = ''
         item.forEach((cell,index) => {
           row+= `${index!==0?'\t':''}${cell||''}`
         })
-        str+=`${row}\n`
+        str+= r_index===rangeArr.length-1 ? row : `${row}\n`
       });
       
       copyFit(str)

+ 15 - 2
src/views/datasheet_manage/components/sheetClassifyDia.vue

@@ -40,7 +40,7 @@
 import * as sheetInterface from '@/api/modules/sheetApi.js';
 import futuresInterface from '@/api/modules/futuresBaseApi';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi.js';
-import { fittingEquationInterface,statisticFeatureInterface } from '@/api/modules/chartRelevanceApi';
+import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
 export default {
 	props: {
 		isOpenDialog: {
@@ -88,7 +88,8 @@ export default {
 				'/commordityChartBase': this.commodityClassifyApi,
 				'/chartrelevance':this.relevanceClassifyApi,
 				'/fittingEquationList': this.fittingEquationClassifyApi,
-				'/statisticFeatureList': this.statisticFeatureClassifyApi
+				'/statisticFeatureList': this.statisticFeatureClassifyApi,
+				'/crossVarietyChartList': this.crossVarietyClassifyApi
 			}
 			handleMap[this.$route.path] && handleMap[this.$route.path](classify_name,classify_id)
 		},
@@ -153,6 +154,18 @@ export default {
 			this.$emit('successCallback')
 		},
 
+		/* 跨品种分析 */
+		async crossVarietyClassifyApi(classify_name,classify_id){
+			const { Ret,Msg } = !classify_id
+				? await crossVarietyInterface.classifyAdd({ ChartClassifyName:classify_name })
+				: await crossVarietyInterface.classifyEdit({ ChartClassifyName:classify_name, ChartClassifyId:classify_id  })
+				
+			if( Ret !== 200) return
+			this.$message.success(Msg);
+			this.cancelHandle();
+			this.$emit('successCallback')
+		},
+
 		/* 取消 */
 		cancelHandle() {
       this.formData = {}

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

@@ -59,6 +59,7 @@ export default {
 </script>
 <style scoped lang='scss'>
   .sheet-list-cont {
+    width: 100%;
     color: #333;
     .el-card .el-card__header,
     .el-card__body {

+ 7 - 1
src/views/login_manage/EmailModel.vue

@@ -73,6 +73,7 @@ export default {
             timer:0,
             codeStr:'获取验证码',
             codeCountDown:60,
+            isGetNewCode:false,
             form:{
                 email:'',
                 picCode:'',
@@ -98,10 +99,13 @@ export default {
     methods: {
         checkForm(){
             //首先检查是否是重新获取验证码
-            if(this.codeStr==='重新获取'){
+            if(this.codeStr==='重新获取'&&!this.isGetNewCode){
                 //引导用户重新输入图形验证码
+                this.$message.warning('重新获取需再次输入图形验证码')
                 this.getCodePic()
                 this.form.picCode = ''
+                this.isGetNewCode = true
+                return
             }
             //检查邮箱和图形验证码是否正确
             const {email,picCode} = this.form
@@ -123,7 +127,9 @@ export default {
             }).then(res=>{
                 if(res.Ret!==200) return 
                 this.$message.success('验证码已发送')
+                this.isGetNewCode = false
                 //60秒倒计时
+                this.codeCountDown = 60
                 this.countDown()
                 this.timer = setInterval(()=>{
                     this.countDown()

+ 8 - 5
src/views/login_manage/MobileModel.vue

@@ -83,6 +83,7 @@ export default {
             timer:0,
             codeStr:'获取验证码',
             codeCountDown:60,
+            isGetNewCode:false,
             form:{
                 mobile:'',
                 picCode:'',
@@ -109,10 +110,13 @@ export default {
     methods: {
         checkForm(){
             //首先检查是否是重新获取验证码
-            if(this.codeStr==='重新获取'){
+            if(this.codeStr==='重新获取'&&!this.isGetNewCode){
                 //引导用户重新输入图形验证码
+                this.$message.warning('重新获取需再次输入图形验证码')
                 this.getCodePic()
                 this.form.picCode = ''
+                this.isGetNewCode = true
+                return
             }
             //检查手机号和图形验证码是否正确
             const {picCode,mobile} = this.form
@@ -124,10 +128,7 @@ export default {
                 this.$message.warning('请输入正确的手机号')
                 return
             }
-            /* this.countDown()
-                this.timer = setInterval(()=>{
-                    this.countDown()
-                },1000) */
+
             //通过请求发送验证码
             departInterence.getCodeVerify({
                 VerifyType:1,
@@ -139,7 +140,9 @@ export default {
             }).then(res=>{
                 if(res.Ret!==200) return 
                 this.$message.success('验证码已发送')
+                this.isGetNewCode = false
                 //60秒倒计时
+                this.codeCountDown = 60
                 this.countDown()
                 this.timer = setInterval(()=>{
                     this.countDown()

+ 1 - 1
src/views/login_manage/modelMixins.js

@@ -21,7 +21,7 @@ export default {
         },
         countDown(){
             /* if(!this.timer) return */
-            console.log('click down')
+            //console.log('click down')
             this.codeCountDown--
             this.codeStr=`重新获取(${this.codeCountDown})秒`
             if(this.codeCountDown<=0){

+ 15 - 1
src/views/mychart_manage/components/chartDetailDia.vue

@@ -122,7 +122,7 @@
               class="span-item"
               style="margin-left: 7px"
               @click="saveChartMapHandle"
-              v-if="![3,4,6,7,8,9].includes(chartInfo.Source)"
+              v-if="[1,2,5].includes(chartInfo.Source)"
             >
               <span> <i class="el-icon-collection" />&nbsp;保存 </span>
             </span>
@@ -750,6 +750,16 @@ export default {
             this.tableData = res.Data.EdbInfoList;
             this.statisticFrequencyData = res.Data.DataResp;
             this.setStatisticFrequency();
+          }else if(this.chartInfo.Source===10) { //跨品种分析
+            this.tableData = res.Data.EdbInfoList;
+            this.crossVarietyChartData = res.Data.DataResp;
+            this.chartLimit = {
+              min: Number(res.Data.DataResp.YMinValue),
+              max: Number(res.Data.DataResp.YMaxValue),
+              x_min: Number(res.Data.DataResp.XMinValue),
+              x_max: Number(res.Data.DataResp.XMaxValue)
+            }
+            this.setCrossVarietyChart();
           }
         });
     },
@@ -900,6 +910,8 @@ export default {
         res=await fittingEquationInterface.refreshChart({ ChartInfoId })
       }else if([7,8,9].includes(Source)) {
         res=await statisticFeatureInterface.refreshChart({ ChartInfoId })
+      }else if(Source===10) {
+        res=await crossVarietyInterface.refreshChart({ ChartInfoId })
       }
      
         this.refreshLoading = false;
@@ -1309,6 +1321,8 @@ export default {
         path='/fittingEquationChartEditor'
       }else if([7,8,9].includes(this.chartInfo.Source)) {
         path='/statisticFeatureChartEditor'
+      }else if(this.chartInfo.Source===10) {
+        path='/crossVarietyChartEditor'
       }
 
 

+ 21 - 20
src/views/mychart_manage/components/chooseChart.vue

@@ -36,6 +36,7 @@
 						<el-radio :label="3">相关性图表</el-radio>
 						<el-radio :label="6">拟合方程曲线</el-radio>
 						<el-radio :label="7">统计特征</el-radio>
+						<el-radio :label="10">跨品种分析</el-radio>
 					</el-radio-group>
           <el-checkbox v-model="isShowMe"  @change="searchHandle">只看我的</el-checkbox>
         </div>
@@ -96,7 +97,7 @@
 import { mychartInterface } from '@/api/api.js';
 import addMyClassifyDia from '@/views/dataEntry_manage/components/addMyClassifyDia';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi';
-import { fittingEquationInterface,statisticFeatureInterface } from '@/api/modules/chartRelevanceApi';
+import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
 import futuresInterface from '@/api/modules/futuresBaseApi';
 export default {
   components: {
@@ -140,26 +141,26 @@ export default {
         KeyWord: this.search_txt,
         IsShowMe: this.isShowMe
       };
-      let res=null 
-      if(this.chart_source===1){
-        res=await mychartInterface.publicList(params)
-      }else if(this.chart_source===2){
-				res=await futuresInterface.searchChart(params)
-			}else if(this.chart_source===3){
-        res=await chartRelevanceApi.getChartList(params)
-      }else if(this.chart_source===6) {
-        res=await fittingEquationInterface.getChartList(params)
-      }else if(this.chart_source===7) {
-        res=await statisticFeatureInterface.getChartList(params)
+
+      const apiMap = {
+        1: mychartInterface.publicList,
+        2: futuresInterface.searchChart,
+        3: chartRelevanceApi.getChartList,
+        6: fittingEquationInterface.getChartList,
+        7: statisticFeatureInterface.getChartList,
+        10: crossVarietyInterface.searchChart,
       }
-        if (res.Ret !== 200) return;
-        this.haveMove = res.Data ? this.page_no < res.Data.Paging.Pages : false;
-        this.chartPublicList = res.Data
-          ? this.page_no === 1
-            ? res.Data.List
-            : [...this.chartPublicList, ...res.Data.List]
-          : [];
-        this.haveData = this.chartPublicList.length ? true : false;
+      let res = await apiMap[this.chart_source](params)
+
+
+      if (res.Ret !== 200) return;
+      this.haveMove = res.Data ? this.page_no < res.Data.Paging.Pages : false;
+      this.chartPublicList = res.Data
+        ? this.page_no === 1
+          ? res.Data.List
+          : [...this.chartPublicList, ...res.Data.List]
+        : [];
+      this.haveData = this.chartPublicList.length ? true : false;
       
     },
 

+ 84 - 8
src/views/mychart_manage/index.vue

@@ -48,8 +48,8 @@
       </div>
       <div class="left-bottom">
         <div class="public-classify" v-if="publicClassifyList.length">
-          <h3 class="classify-type">公共图库</h3>
-          <ul class="public-ul">
+          <h3 @click="expandPublic = !expandPublic" class="classify-type">公共图库<span><i :class="{'el-icon-arrow-down':!expandPublic,'el-icon-arrow-up':expandPublic}"></i></span></h3>
+          <!-- <ul class="public-ul">
             <li
               :class="[
                 'classify-item',
@@ -74,7 +74,32 @@
                 </el-dropdown>
               </div>
             </li>
-          </ul>
+          </ul> -->
+          <div class="tree-wrap" v-show="expandPublic">
+                <el-tree
+                    ref="catalogTree"
+                    class="catalog-tree other-tree"
+                    empty-text="暂无图库"
+                    :data="publicClassifyList"
+                    node-key="nodeKeyId"
+                    :expand-on-click-node="false"
+                    @current-change="(data,node)=>{nodeChange(data,node)}"
+                    >
+                    <div class="custom-tree-node" slot-scope="{ data,node }">
+                        <span class="tree-label">{{ data.name }}</span>
+                        <div class="right-item right-item-box" >
+                            <el-dropdown @command="handleCommand" trigger="click" v-if="isShowDropPublic&&data.MyChartClassifyId">
+                            <span class="el-dropdown-link"> 
+                                <i class="el-icon-more" style="font-size: 16px;transform: rotate(90deg);cursor: pointer"/>
+                            </span>
+                            <el-dropdown-menu slot="dropdown">
+                                <el-dropdown-item :command="{key:'copy'}" :disabled="select_classify_userid === roleId">复制</el-dropdown-item>
+                            </el-dropdown-menu>
+                            </el-dropdown>
+                        </div>
+                    </div>
+                </el-tree>
+            </div>
         </div>
         <div class="classify" v-if="classifyList.length">
           <h3 class="classify-type">我的图库</h3>
@@ -166,7 +191,7 @@
         
         <div class="cont-top">
           <span>共{{ total }}张图表</span>
-          <span v-if="ispublic === 1">分享人: {{ publicClassifyList.find(item => item.MyChartClassifyId === select_classify).RealName}}</span>
+          <span v-if="ispublic === 1">分享人: {{ RealName }}</span>
         </div>
         <div
           class="chart-public-list"
@@ -229,7 +254,7 @@
         </div>
       </div>
       <div v-else class="nodata">
-        <span v-if="ispublic === 1" class="sharer">分享人: {{ publicClassifyList.find(item => item.MyChartClassifyId === select_classify).RealName}}</span>
+        <span v-if="ispublic === 1" class="sharer">分享人: {{ RealName }}</span>
         <tableNoData text="暂无图表"/>
       </div>
     </div>
@@ -324,6 +349,8 @@ export default {
   },
   data() {
     return {
+      expandPublic:false,
+      RealName:'',
       haveData: true,
       isSlideLeft: false, //收起分类
       search_txt: '',
@@ -473,10 +500,36 @@ export default {
     getPublicClassify() {
       mychartInterface.publicClassify().then((res) => {
         if(res.Ret !== 200) return;
-        this.publicClassifyList = res.Data ? res.Data.List.map(item => ({
+        /* this.publicClassifyList = res.Data ? res.Data.List.map(item => ({
           ...item,
           fromPublic: 1
-        })) : [];
+        })) : []; */
+        this.publicClassifyList = res.Data?res.Data.List||[]:[]
+        /* this.publicClassifyList = [{
+            MenuAdminId:204,
+            MenuName:"ficc销售name",
+            Items:[{
+                MyChartClassifyId:370,
+                MyChartClassifyName:"图库2.0",
+                RealName:'ficc销售'
+            }]
+        }] */
+        this.publicClassifyList = this.publicClassifyList.map(list=>{
+            list.name = list.MenuName
+            list.nodeKeyId = 'list' + list.MenuAdminId
+            if(list.Items){
+                list.children = list.Items.map(item=>{
+                    return {
+                        ...item,
+                        ...{
+                            nodeKeyId:'item'+ item.MyChartClassifyId,
+                            name:item.MyChartClassifyName
+                        }
+                    }
+                })
+            }
+            return list
+        })
       })
     },
 
@@ -550,9 +603,28 @@ export default {
 			if(!this.search_have_more) return;
 			this.searchApi(this.current_search,++this.search_page);
 		},
-
+    nodeChange(data,node){
+        if(node.level===1){
+            this.select_classify = 0
+            this.haveData = false
+            this.total = 0
+            this.page_no = 1
+            this.chartList = []
+            this.ispublic=''
+            return
+        }
+        this.RealName = data.RealName
+        this.chooseClassify({
+            MyChartClassifyId:data.MyChartClassifyId,
+            fromPublic:1,
+            AdminId:data.AdminId
+        })
+    },
     /* 切换分类 */
     chooseClassify({MyChartClassifyId,fromPublic,AdminId}) {
+      if(fromPublic!==1){
+        this.$refs.catalogTree.setCurrentKey(null)
+      }
       this.select_classify = MyChartClassifyId;
       this.ispublic = fromPublic;
       this.select_classify_userid = AdminId;
@@ -936,6 +1008,7 @@ export default {
 };
 </script>
 <style lang="scss">
+@import "../chartFrame_manage/css/customTree.scss";
 .mychart_container {
   position: relative;
   .right-item-box {
@@ -1059,6 +1132,9 @@ export default {
         margin-bottom: 20px;
         /* .public-ul {
         } */
+        .tree-wrap{
+            padding:15px;
+        }
       }
     }
     .noclassify {

+ 58 - 21
src/views/operation_manage/AIQA/AIQA.vue

@@ -3,10 +3,10 @@
         <div class="window-list-wrap">
             <div class="window-title">
                 <div class="title-wrap">
-                    <p style="color: #333333;font-size: 20px;font-weight: 600;">HORIZON INSIGHTS</p>
-                    <span style="color: #666666;font-size: 16px;">弘则研究AI问答小助手</span>
+                    <p class="text-ellipsis" style="color: #333333;font-size: 20px;font-weight: 600;">{{companyName}}</p>
+                    <span style="color: #666666;font-size: 16px;">研究AI问答小助手</span>
                 </div>
-                <div class="icon"><img src="~@/assets/img/icons/horizon.png" /></div>
+                <div class="icon"><img src="~@/assets/img/icons/ai_company.png"/></div>
             </div>
             <div class="add-btn" @click="handleAddNewWindow"><i class="el-icon-circle-plus-outline"></i>新建对话窗口</div>
             <div class="list-wrap hidden-scrollbar">
@@ -28,10 +28,10 @@
             <div class="content-header">
                 <div class="title-wrap">
                     <p>{{activeWindowId<=0?'新对话窗口':activeWindow.TopicName||''}}</p>
-                    <span>{{activeWindowId<=0?'弘则AI助手使用说明':`${historyList.length||0} messages`}}</span>
+                    <span>{{activeWindowId<=0?'AI助手使用说明':`${historyList.length||0} messages`}}</span>
                 </div>
                 <div class="select-box">
-                    <el-select v-model="model" :class="{'hint':showHint}" :disabled="isTyping||(windowContentLoading&&windowContentLoading.visible)" ref="modelSelect" 
+                    <!-- <el-select v-model="model" :class="{'hint':showHint}" :disabled="isTyping||(windowContentLoading&&windowContentLoading.visible)" ref="modelSelect" 
                         @click.native="selectClick"
                         @change="changeModel">
                         <el-option v-for="item in modelList" :key="item.label"
@@ -40,7 +40,7 @@
                             <span style="float:left">{{item.label}}</span>
                             <span style="float:right"><img :src="item.icon" style="margin-top:5px;width:24px;height:24px;"/></span>
                         </el-option>
-                    </el-select>
+                    </el-select> -->
                 </div>
             </div>
             <!-- 仅这一部分滚动 -->
@@ -74,6 +74,7 @@ import NewWindowHint from './components/newWindowHint.vue';
 import WindowListItem from './components/windowListItem';
 /* api */
 import {aiQAInterence} from '@/api/modules/aiApi.js'; 
+import {etaBaseConfigInterence} from '@/api/modules/etaBaseConfigApi.js';
 export default {
     components: { WindowListItem,NewWindowHint, MessageItem },
     data() {
@@ -86,17 +87,25 @@ export default {
             /* window-content*/
             historyList:[],//当前窗口历史记录
             inputText:'',
-            model:'',//当前选择的模型
+            model:'GPT-4 Turbo',//当前选择的模型
             modelOldValue:'',
             modelList:[
                 {
-                    label:'gpt-3.5-turbo',
-                    icon:require('@/assets/img/icons/chat-gpt.png'),
+                    label:'GPT-4 Turbo',
+                    icon:require('@/assets/img/icons/gpt-4-turbo.png'),
+                },
+                {
+                    label:'GPT4',
+                    icon:require('@/assets/img/icons/gpt-4.png'),
                 },
                 {
                     label:'gpt-3.5-turbo-16k',
                     icon:require('@/assets/img/icons/chat-gpt-16k.png'),
                 },
+                {
+                    label:'gpt-3.5-turbo',
+                    icon:require('@/assets/img/icons/chat-gpt.png'),
+                },
                 {
                     label:'eta',
                     icon:require('@/assets/img/icons/horizon.png'),
@@ -105,6 +114,8 @@ export default {
             showHint:false,//选择模型提示
             isTyping:false,//是否处于打字动画中
             windowContentLoading:null,
+            answerLoading:false,//回答中
+            companyName:'',
         };
     },
     watch:{
@@ -153,7 +164,7 @@ export default {
                 this.historyList = List||[]
                 this.windowContentLoading&&this.windowContentLoading.close()
                 //使用模型
-                this.model = this.historyList.length?this.historyList[this.historyList.length-1].Model:''
+                this.model = this.historyList.length?this.historyList[this.historyList.length-1].Model:'GPT-4 Turbo'
                 //如果有历史记录,则滚动到底部
                 this.$nextTick(()=>{
                     const windowContentWrap = document.querySelector('.window-content-wrap')
@@ -199,7 +210,7 @@ export default {
             this.activeWindowId=0
             this.activeWindow=null
             this.historyList=[]
-            this.model=''
+            this.model='GPT-4 Turbo'
             //this.inputText=''
             this.isTyping = false
         },
@@ -285,23 +296,24 @@ export default {
                 this.inputText+='\n'
                 return
             }
-            if(this.isTyping){
+            if(this.isTyping||this.answerLoading){
                 this.$message.warning('请等待回答完成')
                 return
             }
             //新建窗口,未选择模型
-            if(this.activeWindowId===0&&this.model===''){
-                this.showHint = true
-                this.$message.error('请选择模型')
-                this.$nextTick(()=>{
-                    this.$refs.modelSelect.focus()
-                })
-                return
-            }
+            // if(this.activeWindowId===0&&this.model===''){
+            //     this.showHint = true
+            //     this.$message.error('请选择模型')
+            //     this.$nextTick(()=>{
+            //         this.$refs.modelSelect.focus()
+            //     })
+            //     return
+            // }
             if(this.inputText.length===0){
                 this.$message.warning('请输入提问')
                 return
             }
+            this.answerLoading=true
             this.activeWindowId===0&&(this.activeWindowId = -1)
             //this.activeWindowId!==0&&this.getWindowDetail()
             //mock 加入到historyList中
@@ -329,8 +341,9 @@ export default {
             aiQAInterence.sendChatMsg({
                 AiChatTopicId:this.activeWindowId<=0?0:this.activeWindowId,
                 Ask:inputText,
-                Model:this.model
+                // Model:this.model
             }).then(res=>{
+                this.answerLoading=false
                 //在回答未获取前切换了新窗口
                 if(this.historyList.length===0){
                     this.getWindowList()
@@ -365,6 +378,12 @@ export default {
                 msg.Model = Model
                 msg.isPlay = true
                 this.historyList.splice(this.historyList.length-1,1,msg)
+            }).catch(()=>{
+                this.answerLoading=false
+                const msg = this.historyList[this.historyList.length-1]
+                msg.Answer = '回答超时,请重试!'
+                msg.isPlay = true
+                this.historyList.splice(this.historyList.length-1,1,msg)
             })
         },
         //获取窗口列表
@@ -378,10 +397,19 @@ export default {
                 this.windowList = res.Data.List||[]
                 this.listWrapLoading&&this.listWrapLoading.close()
             })
+        },
+
+        getBaseConfig(){
+            etaBaseConfigInterence.getBaseConfig().then(res=>{
+                if(res.Ret===200){
+                    this.companyName=res.Data.CompanyName||''
+                }
+            })
         }
     },
     mounted(){
         this.getWindowList()
+        this.getBaseConfig()
     }
 };
 </script>
@@ -422,10 +450,19 @@ $border-color:#3D52A1;
             display: flex;
             justify-content: space-between;
             align-items: center;
+            // overflow: hidden;
             .icon img{
                 width:58px;
                 height:58px;
             }
+            .title-wrap{
+                overflow: hidden;
+            }
+            .text-ellipsis{
+                overflow: hidden;
+                white-space: nowrap;
+                text-overflow: ellipsis;
+            }
         }
         .add-btn{
             border:2px dashed $border-color;

+ 6 - 4
src/views/operation_manage/AIQA/components/messageItem.vue

@@ -28,10 +28,12 @@ export default {
     data() {
         return {
             iconMap:{
-                'user':require('@/assets/img/set_m/user_img.png'),
+                'user':require('@/assets/img/icons/ai_avatar.png'),
                 'gpt-3.5-turbo':require('@/assets/img/icons/chat-gpt.png'),
                 'gpt-3.5-turbo-16k':require('@/assets/img/icons/chat-gpt-16k.png'),
-                'eta':require('@/assets/img/icons/horizon.png')
+                'eta':require('@/assets/img/icons/horizon.png'),
+                'GPT4':require('@/assets/img/icons/gpt-4.png'),
+                'GPT-4 Turbo':require('@/assets/img/icons/gpt-4-turbo.png'),
             },
             isPlay:false,//是否播放动画
             typingText:'',
@@ -51,13 +53,13 @@ export default {
     computed:{
         IconSrc(){
             const {messageType,modelName} = this.messageInfo
-            const iconType = messageType==='question'?'user':modelName
+            const iconType = messageType==='question'?'user':'GPT-4 Turbo'
             return this.iconMap[iconType]||''
         }
     },
     methods: {
         async playTyping(){
-            const writeText = (text,delay=150)=>{
+            const writeText = (text,delay=20)=>{
                 return new Promise((res,rej)=>{
                     setTimeout(()=>{
                         this.typingText+=text

+ 2 - 3
src/views/operation_manage/AIQA/components/newWindowHint.vue

@@ -1,9 +1,8 @@
 <template>
     <div class="new-window-hint-wrap">
         <p class="title">使用说明:</p>
-        <p>1、每账号每天最高50次问答。</p>
-        <p>2、在同一个对话窗口内提问,选择同一模型可联系上下文回答,切换模型后不支持。</p>
-        <p>3、历史问答默认用该对话窗口内第一个提问命名,可修改。</p>
+        <p>1、每账号每天最高500次问答。</p>
+        <p>2、历史问答默认用该对话窗口内第一个提问命名,可修改。</p>
     </div>
 </template>
 

+ 1 - 1
src/views/operation_manage/AIQA/components/windowListItem.vue

@@ -1,5 +1,5 @@
 <template>
-    <div class="list-item" :class="{'active':activeWindowId===item.AiChatTopicId}">
+    <div class="list-item" :class="[activeWindowId===item.AiChatTopicId?'active':'']">
         <template v-if="!isEdit">
             <p>{{item.TopicName}}</p>
             <!-- <span>6 messages </span> -->

+ 116 - 1
src/views/ppt_manage/mixins/mixins.js

@@ -72,7 +72,10 @@ export default {
       },
 
       /* 统计频率图 */
-      statisticFrequencyData: {}
+      statisticFrequencyData: {},
+
+      /* 跨品种分析图 */
+      crossVarietyChartData: {}
     }
   },
   methods: {
@@ -1331,6 +1334,118 @@ export default {
       }
     },
 
+     /* 跨品种分析 */
+     setCrossVarietyChart() {
+      
+      const { min,max,x_min,x_max }  = this.chartLimit;
+
+      const { DataList,XName,YName,XNameEn,YNameEn } = this.crossVarietyChartData;
+
+      //y轴
+      let yAxis = {
+        ...basicYAxis,
+        title: {
+          text: YName,
+          textCh:YName,// 中文
+          textEn:YNameEn||YName,
+          align: 'middle',
+        },
+        opposite: false,
+        reversed: false,
+        min: Number(min),
+        max: Number(max),
+        tickWidth: 1,
+      }
+
+      // x轴
+      let xAxis = {
+        ...scatterXAxis,
+        title: {
+          text: XName,
+          textCh:XName,// 中文
+          textEn:XNameEn || XName,
+          align: 'middle',
+        },
+        min: Number(x_min),
+        max: Number(x_max),
+      }
+
+       //数据列
+      let series = [];
+      DataList.forEach(item => {
+        //数据列
+        let series_item = {
+          data: [],
+          type: 'scatter',
+          name: item.Name,
+          nameCh: item.Name,
+          nameEn: item.NameEn||item.Name,
+          color: item.Color,
+          lineWidth: 0,
+          chartType: 'linear',
+          zIndex:1
+        }
+        item.CoordinatePointData.forEach(_ => {
+          series_item.data.push({x: _.X,y: _.Y,})
+        })
+        series.push(series_item);
+      })
+
+      let edbInfoList = this.dataList;
+      let tooltip = {
+        formatter: function() {
+          let series_obj = DataList.find(_ => _.Name === this.series.name);
+          let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===this.x && _.Y===this.y);
+          
+          let xEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId);
+          let yEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId);
+
+          let str=`<b>${ this.series.name }</b>`;
+          str += `<br><span style="color:${this.color}">\u25CF</span>${xEdbInfo.EdbName}: ${this.x} ${ponit_obj.XDate}<br>`;
+          str += `<span style="color:${this.color}">\u25CF</span>${yEdbInfo.EdbName}: ${this.y} ${ponit_obj.YDate}`;
+
+          return str
+        },
+        formatterCh: function() {
+          let series_obj = DataList.find(_ => _.Name === this.series.name);
+          let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===this.x && _.Y===this.y);
+          
+          let xEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId);
+          let yEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId);
+
+          let str=`<b>${ this.series.name }</b>`;
+          str += `<br><span style="color:${this.color}">\u25CF</span>${xEdbInfo.EdbName}: ${this.x} ${ponit_obj.XDate}<br>`;
+          str += `<span style="color:${this.color}">\u25CF</span>${yEdbInfo.EdbName}: ${this.y} ${ponit_obj.YDate}`;
+
+          return str
+          
+        },
+        formatterEn: function() {
+          let series_obj = DataList.find(_ => _.NameEn === this.series.name);
+          let ponit_obj = series_obj.CoordinatePointData.find(_ => _.X ===this.x && _.Y===this.y);
+          
+          let xEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.XEdbInfoId);
+          let yEdbInfo = edbInfoList.find(_ => _.EdbInfoId===ponit_obj.YEdbInfoId);
+
+          let str=`<b>${ this.series.name }</b>`;
+          str += `<br><span style="color:${this.color}">\u25CF</span>${xEdbInfo.EdbNameEn}: ${this.x} ${ponit_obj.XDate}<br>`;
+          str += `<span style="color:${this.color}">\u25CF</span>${yEdbInfo.EdbNameEn}: ${this.y} ${ponit_obj.YDate}`;
+
+          return str
+        }
+      }
+
+      this.options = {
+        title: {
+          text:''
+        },
+        series,
+        yAxis: [yAxis],
+        xAxis,
+        tooltip
+      }
+    },
+
     /* 查询范围为1年内 x轴显示为月/日 否则默认年/月 */
     xTimeDiffer() {
       const end_date =

+ 107 - 18
src/views/ppt_manage/mixins/pptMixins.js

@@ -7,7 +7,7 @@ import {etaBaseConfigInterence} from '@/api/modules/etaBaseConfigApi.js';
 import {pptEnInterface} from '@/api/modules/pptEnApi.js';
 import * as sheetInterface from '@/api/modules/sheetApi.js';
 import futuresInterface from '@/api/modules/futuresBaseApi';
-import { fittingEquationInterface,statisticFeatureInterface } from '@/api/modules/chartRelevanceApi';
+import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi.js';
 import { defaultOpts } from '@/utils/defaultOptions';
 import {formatPPTDate,checkPPTpageElemant,getStrSize,isShowPPTTitle} from '../newVersion/utils/untils.js';
@@ -98,7 +98,8 @@ export default {
 			TemplateType,
       ReportId,
       ModifyTime,
-      PublishTime
+      PublishTime,
+      Editor
 		} = res.Data
       const pptDate = formatPPTDate(this.currentLang,PptDate)
       let legalContent = JSON.parse(Content)
@@ -118,13 +119,24 @@ export default {
 			},
       ReportId,
       ModifyTime,
-      PublishTime
+      PublishTime,Editor
 		}
       }else{
         this.result = {status:'',content:'获取ppt数据失败!'}
       }
     },
-    //ppt设置相关,引用mixins.js里的setOptions
+    //ppt设置相关,引用mixins.js里的setOptions chartType只有source是1才有用
+    /* Source 1 ; chartType 1曲线 2季节 3面积 4堆积柱 5散点 6组合 7柱形 8截面散点
+    * 2 商品价格
+    * 3 相关性
+    * 4 滚动相关性
+    * 5 商品利润
+    * 6 拟合方程
+    * 7 统计特征/标准差
+    * 8 统计特征/百分位
+    * 9 统计特征/频率
+    * 10 跨品种分析
+    */
     //获取图表数据
     async getchartData(id,lang='ch') {
       const currentLang = this.currentLang || lang
@@ -187,6 +199,17 @@ export default {
         this.dataList = res.Data.EdbInfoList;
         this.statisticFrequencyData = res.Data.DataResp;
         this.setStatisticFrequency();
+      }else if(this.chartInfo.Source===10) {
+        this.dataList = res.Data.EdbInfoList;
+        this.crossVarietyChartData = res.Data.DataResp;
+        this.chartLimit = {
+          min: Number(res.Data.DataResp.YMinValue),
+          max: Number(res.Data.DataResp.YMaxValue),
+          x_min: Number(res.Data.DataResp.XMinValue),
+          x_max: Number(res.Data.DataResp.XMaxValue)
+        }
+        this.setCrossVarietyChart();
+        this.changeRelevanceOptions();//更改英文一样
       }
 
       //根据图表数据获取配置项
@@ -299,16 +322,25 @@ export default {
       if(!this.chartInfo.ChartNameEn){
         result = false
       }
-      //指标名称:this.dataList[].EdbNameEn
-      //指标单位:this.dataList[].UnitEn
-      this.dataList.forEach(item=>{
-        if(!item.EdbNameEn){
-          result = false
-        }
-        if(this.chartInfo.ChartType!==10&&item.Unit&&!item.UnitEn){
-          result = false
-        }
-      })
+
+      if(this.chartInfo.Source === 10) {
+        const { XNameEn,YNameEn,DataList } = this.crossVarietyChartData;
+
+        let haveOneNoEn = DataList.some(_ =>!_.NameEn);
+
+        if(haveOneNoEn || !XNameEn || !YNameEn) result = false
+      }else {
+        //指标名称:this.dataList[].EdbNameEn
+        //指标单位:this.dataList[].UnitEn
+        this.dataList.forEach(item=>{
+          if(!item.EdbNameEn){
+            result = false
+          }
+          if(this.chartInfo.ChartType!==10&&item.Unit&&!item.UnitEn){
+            result = false
+          }
+        })
+      }
       return result
     },
     //刷新图表数据
@@ -608,6 +640,7 @@ export default {
         value:chartInfo.ChartName,
         key:'ChartName',
         id:chartInfo.ChartInfoId,
+        source: chartInfo.Source,
         notEdit:true
       },
       {
@@ -617,7 +650,7 @@ export default {
         id:chartInfo.ChartInfoId,
         placeholder:'请输入英文图表名称'
       })
-      if(![3,6,7,8,9].includes(chartInfo.Source)){
+      if([1,2,5].includes(chartInfo.Source)){
         this.formItemArray.chartsList = tableData.map(item => {
           return item.Unit 
             ? [
@@ -688,7 +721,7 @@ export default {
       }
 
       //利润曲线
-      if(chartInfo.Source===5) {
+      else if(chartInfo.Source===5) {
         this.formItemArray.chartInfo.push({
           label:'盘面利润名称',
           value:Data.DataResp.ProfitName,
@@ -704,6 +737,60 @@ export default {
           placeholder:'请输入英文盘面利润名称'
         })
       }
+
+      //跨品种分析
+      else if(chartInfo.Source===10) {
+        let res = await crossVarietyInterface.chartLangOption({ChartInfoId: chartInfo.ChartInfoId})
+
+        const { TagList,VarietyList } = res.Data;
+
+        this.formItemArray.chartInfo.push({
+          label:'X轴名称',
+          value:Data.DataResp.XName,
+          key:'XName',
+          id:TagList[0].ChartTagId,
+          notEdit:true
+        },
+        {
+          label:'英文X轴名称',
+          value:Data.DataResp.XNameEn,
+          key:'XNameEn',
+          id:TagList[0].ChartTagId,
+          placeholder:'请输入英文X轴名称'
+        },{
+          label:'Y轴名称',
+          value:Data.DataResp.YName,
+          key:'YName',
+          id:TagList[1].ChartTagId,
+          notEdit:true
+        },
+        {
+          label:'英文Y轴名称',
+          value:Data.DataResp.YNameEn,
+          key:'YNameEn',
+          id:TagList[1].ChartTagId,
+          placeholder:'请输入英文Y轴名称'
+        })
+
+        VarietyList.forEach(item => {
+          this.formItemArray.chartsList.push([
+            {
+              label:'品种名称',
+              value:item.ChartVarietyName,
+              key:'ChartVarietyName',
+              id:item.ChartVarietyId,
+              notEdit:true
+            },
+            {
+              label:'英文品种名称',
+              value:item.ChartVarietyNameEn,
+              key:'ChartVarietyNameEn',
+              id:item.ChartVarietyId,
+              placeholder:'请输入英文品种名称'
+            }
+          ])
+        })
+      }
 			
       this.setEnName = true
 		},
@@ -727,16 +814,18 @@ export default {
           ChartInfoId: enNameData.ChartInfoId,
           ChartNameEn: enNameData.ChartNameEn
         })
-      }else if(this.chartInfo.Source===6){//拟合方程
+      }else if(this.chart_source===6){//拟合方程
         res=await fittingEquationInterface.editChartEn({
           ChartInfoId: enNameData.ChartInfoId,
           ChartNameEn: enNameData.ChartNameEn
         })
-      }else if(this.chartInfo.Source===7){//统计特征
+      }else if(this.chart_source===7){//统计特征
         res=await statisticFeatureInterface.editChartEn({
           ChartInfoId: enNameData.ChartInfoId,
           ChartNameEn: enNameData.ChartNameEn
         })
+      }else if(this.chart_source===10) {//跨品种分析
+        res=await crossVarietyInterface.editChartEn(enNameData)
       }
       
 			if(res.Ret !==200) return

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

@@ -173,6 +173,7 @@ export default {
           this.firstPage.BackIndex = FirstPage.TemplateType-1
           this.firstPage.imgLocalUrl = this.bgList[FirstPage.TemplateType-1]
           this.pptTime = this.pptItem.PptxUrl?PublishTime:ModifyTime
+          this.$emit('resetToolList',this.result)
         }else{
           this.$message.error('获取ppt数据失败!')
           this.dataLoading.close();

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

@@ -168,6 +168,7 @@ export default {
           this.firstPage.BackIndex = FirstPage.TemplateType-1
           this.firstPage.imgLocalUrl = pptCoverEn[FirstPage.TemplateType-1]
           this.pptTime = this.pptItem.PptxUrl?PublishTime:ModifyTime
+          this.$emit('resetToolList',this.result)
         }else{
           this.$message.error('获取ppt数据失败!')
           this.dataLoading.close();

+ 25 - 2
src/views/ppt_manage/newVersion/pptCatalog.vue

@@ -240,6 +240,7 @@
             :pptItem="pptItem"
             :key="pptItem.PptId"
             @isLegal="handleIsLegal"
+            @resetToolList="setToolList"
           ></ppt-content>
           <ppt-content-old 
             v-else
@@ -545,7 +546,7 @@ export default {
       }
     },
     //设置操作按钮
-    setToolList(){
+    setToolList(pptDetail={}){
       //公共目录:显示除 删除, 编辑 外的全部按钮
       if(this.treeName==='public'){
         this.pptToolList = toolList.filter((i)=>{
@@ -592,6 +593,19 @@ export default {
       this.pptToolList = this.pptToolList.filter((i)=>{
           return authArr.includes(i.key)
         })
+      //若有人在编辑,则显示编辑中
+      if(this.pptToolList.find(i=>i.key==='edit')){
+        const {Editor={}} = pptDetail
+        let labelName = '编辑'
+        if(Editor.IsEditing&&Editor.AdminId!==Number(localStorage.getItem('AdminId'))){
+            labelName = `${Editor.Editor}编辑中...`
+        }
+        this.pptToolList.map(i=>{
+            if(i.key==='edit'){
+                i.label = labelName
+            }
+        })
+      }
     },
     //判断公共目录按钮是否显示
     checkNode(data){
@@ -722,7 +736,7 @@ export default {
       })
     },
     //点击操作栏事件处理
-    handleToolClick(item){
+    async handleToolClick(item){
       const urlMap = {
         edit:`/ppteditor?id=${this.pptItem.PptId}`,
         publish:`/pptpublish?id=${this.pptItem.PptId}`,
@@ -732,6 +746,15 @@ export default {
       if(urlMap[item.key]){
         if(this.pptItem.PptVersion===2){
           if(!this.pptItem.overLimit||item.key!=='publish'){
+            if(item.key==='edit'){
+                if(item.label.length>2){
+                    this.$message.warning("他人正在编辑中,不可重复编辑")
+                    return
+                }
+                //调接口加锁然后跳转
+                const res = await pptInterface.pptEditLock({PptId:Number(this.pptItem.PptId),Status:1})
+                if(res.Ret!==200) return
+            }
             window.open(urlMap[item.key],'_blank');
           }else if(item.key==='publish'){
             //this.$message.warning('该PPT页数超过60页或图表数量超过100张,无法发布,请修改后重试')

+ 10 - 12
src/views/ppt_manage/newVersion/pptEditor.vue

@@ -129,6 +129,7 @@
                   <el-radio :label="3" style="margin-bottom:5px">相关性图表</el-radio>
                   <el-radio :label="6" style="margin-bottom:5px">拟合方程曲线</el-radio>
                   <el-radio :label="7" style="margin-bottom:5px">统计特征</el-radio>
+                  <el-radio :label="10">跨品种分析</el-radio>
                 </el-radio-group>
                 <div style="margin: 10px 0">
                   <el-checkbox v-model="isShowMe" @change="() => {search_page=1;$refs.chartListRef.scrollTop = 0;getreportlist()}">只看我的</el-checkbox>
@@ -270,7 +271,7 @@ import {modelInfo,defaultPosition} from './utils/config'
 import { dataBaseInterface ,sandInterface } from "@/api/api.js";
 import futuresInterface from '@/api/modules/futuresBaseApi';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi';
-import { fittingEquationInterface,statisticFeatureInterface } from '@/api/modules/chartRelevanceApi';
+import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
 import pptmixin from '../mixins/pptMixins';
 import {uploadFileDirect} from "@/utils/common.js"
 import mixins from '../mixins/mixins';
@@ -735,18 +736,15 @@ export default {
         PageSize: this.search_page_sizes,
         IsShowMe: this.isShowMe
       }
-      let res=null
-      if(this.chart_source === 1){
-        res=await dataBaseInterface.chartSearchByEs(params)
-      }else if(this.chart_source === 2){
-        res=await futuresInterface.searchChart(params)
-      }else if(this.chart_source === 3){
-        res=await chartRelevanceApi.searchChart(params)
-      }else if(this.chart_source === 6){
-        res=await fittingEquationInterface.searchChart(params)
-      }else if(this.chart_source === 7){
-        res=await statisticFeatureInterface.searchChart(params)
+      const apiMap = {
+        1: dataBaseInterface.chartSearchByEs,
+        2: futuresInterface.searchChart,
+        3: chartRelevanceApi.searchChart,
+        6: fittingEquationInterface.searchChart,
+        7: statisticFeatureInterface.searchChart,
+        10: crossVarietyInterface.searchChart,
       }
+      let res = await apiMap[this.chart_source](params)
 
       if (res.Ret !== 200) return;
       res.Data.List = res.Data.List || [];

+ 25 - 2
src/views/ppt_manage/newVersion/pptEnCatalog.vue

@@ -234,6 +234,7 @@
             :pptItem="pptItem"
             :key="pptItem.PptId"
             @isLegal="handleIsLegal"
+            @resetToolList="setToolList"
           ></ppt-content>
         </div>
         <div class="tool-side ppt-page-wrap">
@@ -518,7 +519,7 @@ export default {
       }
     },
     //设置操作按钮
-    setToolList(){
+    setToolList(pptDetail={}){
       //公共目录:显示除 删除, 编辑 外的全部按钮
       if(this.treeName==='public'){
         this.pptToolList = toolList.filter((i)=>{
@@ -560,6 +561,19 @@ export default {
       this.pptToolList = this.pptToolList.filter((i)=>{
           return authArr.includes(i.key)
         })
+      //若有人在编辑,则显示编辑中
+      if(this.pptToolList.find(i=>i.key==='edit')){
+        const {Editor={}} = pptDetail
+        let labelName = '编辑'
+        if(Editor.IsEditing&&Editor.AdminId!==Number(localStorage.getItem('AdminId'))){
+            labelName = `${Editor.Editor}编辑中...`
+        }
+        this.pptToolList.map(i=>{
+            if(i.key==='edit'){
+                i.label = labelName
+            }
+        })
+      }  
 
     },
     //判断公共目录按钮是否显示
@@ -688,7 +702,7 @@ export default {
       })
     },
     //点击操作栏事件处理
-    handleToolClick(item){
+    async handleToolClick(item){
       const urlMap = {
         edit:`/ppteneditor?id=${this.pptItem.PptId}`,
         publish:`/pptenpublish?id=${this.pptItem.PptId}`,
@@ -697,6 +711,15 @@ export default {
       //要跳转页面的:预览发布,编辑,演示
       if(urlMap[item.key]){
         if(!this.pptItem.overLimit||item.key!=='publish'){
+            if(item.key==='edit'){
+                if(item.label.length>2){
+                    this.$message.warning("他人正在编辑中,不可重复编辑")
+                    return
+                }
+                //调接口加锁然后跳转
+                const res = await pptEnInterface.pptEditLock({PptId:Number(this.pptItem.PptId),Status:1})
+                if(res.Ret!==200) return
+            }
           window.open(urlMap[item.key],'_blank');
         }else if(item.key==='publish'){
           if(!this.pptItem.overLimitHint.chartNum){

+ 23 - 12
src/views/ppt_manage/newVersion/pptEnEditor.vue

@@ -126,6 +126,7 @@
                   <el-radio :label="3" style="margin-bottom:5px">相关性图表</el-radio> 
                   <el-radio :label="6" style="margin-bottom:5px">拟合方程曲线</el-radio>
                   <el-radio :label="7" style="margin-bottom:5px">统计特征</el-radio>
+                  <el-radio :label="10">跨品种分析</el-radio>
                 </el-radio-group>
                 <div style="margin: 10px 0">
                   <el-checkbox v-model="isShowMe" @change="() => {search_page=1;$refs.chartListRef.scrollTop = 0;getreportlist()}">只看我的</el-checkbox>
@@ -281,7 +282,7 @@ import {modelInfo,defaultPosition} from './utils/config'
 import { dataBaseInterface ,sandInterface } from "@/api/api.js";
 import futuresInterface from '@/api/modules/futuresBaseApi';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi';
-import { fittingEquationInterface,statisticFeatureInterface } from '@/api/modules/chartRelevanceApi';
+import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
 import pptmixin from '../mixins/pptMixins';
 import mixins from '../mixins/mixins';
 import layerMixins from '../mixins/layerMixins';
@@ -729,18 +730,28 @@ export default {
           PageSize: this.search_page_sizes,
           IsShowMe: this.isShowMe
         }
-        let res=null
-        if(this.chart_source === 1){
-          res=await dataBaseInterface.chartSearchByEs(params)
-        }else if(this.chart_source === 2){
-          res=await futuresInterface.searchChart(params)
-        }else if(this.chart_source === 3){
-          res=await chartRelevanceApi.searchChart(params)
-        }else if(this.chart_source === 6){
-          res=await fittingEquationInterface.searchChart(params)
-        }else if(this.chart_source === 7){
-          res=await statisticFeatureInterface.searchChart(params)
+        // let res=null
+        // if(this.chart_source === 1){
+        //   res=await dataBaseInterface.chartSearchByEs(params)
+        // }else if(this.chart_source === 2){
+        //   res=await futuresInterface.searchChart(params)
+        // }else if(this.chart_source === 3){
+        //   res=await chartRelevanceApi.searchChart(params)
+        // }else if(this.chart_source === 6){
+        //   res=await fittingEquationInterface.searchChart(params)
+        // }else if(this.chart_source === 7){
+        //   res=await statisticFeatureInterface.searchChart(params)
+        // }
+
+        const apiMap = {
+          1: dataBaseInterface.chartSearchByEs,
+          2: futuresInterface.searchChart,
+          3: chartRelevanceApi.searchChart,
+          6: fittingEquationInterface.searchChart,
+          7: statisticFeatureInterface.searchChart,
+          10: crossVarietyInterface.searchChart,
         }
+        let res = await apiMap[this.chart_source](params)
 
         if (res.Ret !== 200) return;
         res.Data.List = res.Data.List || [];

+ 19 - 7
src/views/predictEdb_manage/addPredicEdb.vue

@@ -404,7 +404,7 @@
     <dynamic-differ
       :isOpenDialog.sync="isOpenDialog"
       :edbList="dynamicDifferList"
-      :dialog_formula="dialog_formula"
+      :info="dynamicDifferInfo"
       @ensureBack="saveDynamicDifferRule"
       @lookHistory="id => {isLookHistory=true;lookEdbId=id;}"
     />
@@ -569,7 +569,7 @@ export default {
       /* 动态环差弹窗 */
       isOpenDialog: false,
       dynamicDifferList: [],//依赖指标
-      dialog_formula: '',
+      dynamicDifferInfo: '',
       click_rule_index: -1,
       
       select_year: '',
@@ -646,6 +646,8 @@ export default {
             fit_date: _.RuleType === 14 ? [JSON.parse(_.Value).StartDate,JSON.parse(_.Value).EndDate] : [],
             distribute_type: _.RuleType === 16?JSON.parse(_.Value).Type:1,
             on_year: _.RuleType === 16?JSON.parse(_.Value).Year.toString():'',
+            nullValueWay: _.EmptyType,
+            maxNullWay: _.MaxEmptyType
           }))
 
           this.searchOptions = RuleList.map(item => ({
@@ -828,7 +830,9 @@ export default {
               ? _.edbarr.map(item => ({ EdbInfoId:item.target,FromTag:item.tag })) 
               : _.predict_type === 14
               ? [{EdbInfoId: _.self_target,FromTag: ''}]
-              :[]
+              :[],
+            EmptyType: _.predict_type===9 ? _.nullValueWay : 0,
+            MaxEmptyType: _.predict_type===9 ? _.maxNullWay : 0
           }
         })
 
@@ -932,7 +936,9 @@ export default {
             ? _.edbarr.map(item => ({ EdbInfoId:item.target,FromTag:item.tag })) 
             : _.predict_type === 14
             ? [{EdbInfoId: _.self_target,FromTag: ''}]
-            :[]
+            :[],
+          EmptyType: _.predict_type===9 ? _.nullValueWay : 0,
+          MaxEmptyType: _.predict_type===9 ? _.maxNullWay : 0
         }
       })
       let params = {
@@ -1120,18 +1126,24 @@ export default {
     },
 
     /* 设置环比增加值 */
-    setRingAddHandle({edbarr,fixedValue},index) {
+    setRingAddHandle({edbarr,fixedValue,nullValueWay,maxNullWay},index) {
       this.click_rule_index = index;
       this.dynamicDifferList = edbarr;
-      this.dialog_formula =  fixedValue;
+      this.dynamicDifferInfo =  {
+        formulaList: fixedValue,
+        nullValueWay,
+        maxNullWay
+      };
       this.isOpenDialog = true;
     },
     
 
     /* 规则动态环差信息 */
-    saveDynamicDifferRule({arr,formula}) {
+    saveDynamicDifferRule({arr,formula,nullValueWay,maxNullWay}) {
       this.rulesArr[this.click_rule_index].edbarr = arr;
       this.rulesArr[this.click_rule_index].fixedValue = formula;
+      this.rulesArr[this.click_rule_index].nullValueWay = nullValueWay;
+      this.rulesArr[this.click_rule_index].maxNullWay = maxNullWay;
       console.log(this.rulesArr)
     },
 

+ 216 - 17
src/views/predictEdb_manage/components/dynamicRingdiffer.vue

@@ -37,6 +37,9 @@
 							:label="item.EdbName"
 							:value="item.EdbInfoId"
 						>
+							<edbDetailPopover :info="item">
+								<div slot="reference">{{item.EdbName}}</div>
+							</edbDetailPopover>
 						</el-option>
 					</el-select>
 					<i class="el-icon-tickets" style="color:#409EFF;font-size:18px" @click="$emit('lookHistory',list.target)" v-if="list.target"/>
@@ -58,15 +61,93 @@
 				添加更多参数
 			</span>
 			<div class="computed-min">
-				<div class="computed-top">
-					<span>计算公式</span>
-					<el-input placeholder="请输入公式" v-model="formula" clearable style="margin: 0 8px"/>
-          <el-button type="primary" @click="getResult">一键计算</el-button>
+				
+				<div class="computed-section">
+					<div>
+						<label class="label">空值处理
+							<el-tooltip placement="top">
+								<div slot="content" v-html="formTips['null-val']" style="width:300px;line-height:20px;"/>
+								<i class="el-icon-question"/>
+							</el-tooltip>
+						</label>
+						<el-select
+							v-model="nullValueForm.nullValueWay"
+							placeholder="请选择"
+						>
+							<el-option
+								v-for="item in nullWayOptions"
+								:key="item.value"
+								:label="item.label"
+								:value="item.value"
+							>
+							</el-option>
+						</el-select>
+					</div>
+
+					<div style="margin-left: 120px" v-if="showMaxNullDeal">
+						<label class="label">MAX、MIN空值处理
+							<el-tooltip placement="top">
+								<div slot="content" v-html="formTips['max-null-val']" style="width:300px;line-height:20px;"/>
+								<i class="el-icon-question"/>
+							</el-tooltip>
+						</label>
+						<el-select
+							v-model="nullValueForm.maxNullWay"
+							placeholder="请选择"
+						>
+							<el-option label="等于0" :value="1" />
+							<el-option label="跳过空值" :value="2" />
+						</el-select>
+					</div>
+
+          <el-button type="primary" @click="getResult" style="margin-left: 10px">一键计算</el-button>
 				</div>
-				<span class="example-txt"
-					>公式示例:A*0.5+B*C*1.2+120-MAX(A,B,C)</span
-				>
-				<span class="example-txt">函数支持:MAX(),MIN()</span>
+
+				<div class="computed-section">
+					<label class="label">计算公式
+						<el-tooltip placement="top">
+							<div slot="content" v-html="formTips['formula']" style="width:300px;line-height:20px;"/>
+							<i class="el-icon-question"/>
+						</el-tooltip>
+					</label>
+
+					<ul class="formula-list">
+						<li style="margin-bottom: 15px;">
+							<el-input placeholder="请输入公式" v-model="formulaList[0].formula" clearable style="width: 220px"/>
+							
+							<span v-if="formulaDateArr.length" class="date-section-text">{{formulaDateArr[formulaDateArr.length-1]}}(含)之后</span>
+
+							<span class="example-txt">公式示例:A*0.5+B*C*1.2+120-MAX(A,B,C) &nbsp;函数支持:MAX(),MIN(),ln(A),log(a,A),abs(),exp(),pow(),round()</span>
+						</li>
+
+						<li class="formula-item" v-for="(item,index) in formulaList.slice(1)" :key="index+1">
+							<el-input 
+								placeholder="请输入公式" 
+								v-model="item.formula" 
+								clearable
+								style="width: 220px"
+							/>
+
+							<el-date-picker
+								v-model="item.date"
+								type="date"
+								value-format="yyyy-MM-dd"
+								style="margin: 0 10px;width: 220px"
+								placeholder="选择日期"
+								@change="selectFormulaDate($event,item)"
+							/>
+
+							<i class="el-icon-circle-close" style="font-size:20px;" @click="removeFormulaItem(index+1)"/>
+
+							<template v-if="formulaDateArr.length&&item.date">
+								<span v-if="item.date===formulaDateArr[0]" class="date-section-text">{{formulaDateArr[0]}}之前</span>
+								<span v-else class="date-section-text">{{formulaDateArr[formulaDateArr.findIndex(_ =>_ ===item.date)-1]}}(含)——{{item.date}}</span>
+							</template>
+						</li>
+					</ul>
+				</div>
+
+				<el-button icon="el-icon-plus" style="margin-left:70px;" @click="addFormulaHandle">新增分段</el-button>
 			</div>
       
       <el-table
@@ -118,19 +199,40 @@ export default {
 			type: String,
 			default: '设置环比增加值',
 		},
-		dialog_formula: {
-			type: String,
+		info: {
+			type: Object,
 		},
 		edbList: {
 			type: Array,
 		},
 	},
+	computed: {
+		/* max空值处理显示 当输入的公式包含MAX、MIN且空值处理为0时,输入公式失焦后出现右侧选项; */
+		showMaxNullDeal() {
+			let haveMaxOrMin = this.formulaList.some(_ => _.formula.toUpperCase().includes('MAX') || _.formula.toUpperCase().includes('MIN'))
+
+			return haveMaxOrMin && this.nullValueForm.nullValueWay===4
+		},
+
+		formulaDateArr() {
+			return this.formulaList.map(_ => _.date).filter(_ => _).sort((a,b) => new Date(a)-new Date(b))
+		}
+	},
 	watch: {
 		isOpenDialog(newval) {
 			/* 回显 */
 			if (this.edbList.length && newval) {
 				this.addList = _.cloneDeep(this.edbList);
-				this.formula = this.dialog_formula;
+				const{ formulaList,nullValueWay,maxNullWay } = this.info;
+				this.formulaList = JSON.parse(formulaList).map(_=>({
+					formula: _.f,
+					date: _.d
+				}));
+
+				this.nullValueForm = {
+					nullValueWay,
+					maxNullWay
+				}
 
 				this.searchOptions = this.edbList.map(item => ({
 					EdbInfoId: item.target,
@@ -190,6 +292,35 @@ export default {
 				},
       ],
 
+			formulaList: [
+				{ formula: '', }
+			],
+			nullValueForm: {
+				nullValueWay: 0,
+				maxNullWay: 1
+			},//空值处理
+			nullWayOptions: [
+				{ label: '查找前后35天最近值',value: 0 },
+				{ label: '不计算',value: 1 },
+				{ label: '前值填充',value: 2 },
+				{ label: '后值填充',value: 3 },
+				{ label: '等于0',value: 4 },
+			],
+			formTips: {
+				'null-val': `1、查找前后35天最近值:在参与计算的日期序列上某指标无值时,该指标往前/往后找距离最近的值作为当天的值进行计算,遍历允许跨年,往前最多35天,往后最多35天<br>
+				2、不计算:只要有一个指标在某个日期没有值(即空值),则计算指标在该日期没有值 <br>
+				3、前值填充:空值优先以最近的前值填充,没有前值时,用后值填充 <br>
+				4、后值填充:空值优先以最近的后值填充,没有前值时,用后值填充 <br>
+				5、等于0:空值以0值参与计算 <br>
+				注意:此处缺失值的处理,作用于数据全部时间段`,
+				'max-null-val': `MAX、MIN公式中指标存在空值时按如下规则处理:<br>
+				1、等于0,空值用0参与计算;<br>
+				2、跳过空值,去除空值指标,剩余指标进行计算,若该日期所有指标均为空值,则该日期无值;`,
+				'formula':`1、支持新增分段,实现不同分段使用不同的计算公式,若未新增分段,则所有日期序列用统一公式计算<br>
+				2、新增分段需配置新公式和时间节点,在时间节点之前(不含)使用新公式,在时间节点之后(含)使用已配置公式,每个分段公式支持修改<br>
+				3、分段时间节点不允许重复,不允许超出第一个指标的日期区间`
+			},
+
       resultData: [],//
 		};
 	},
@@ -269,27 +400,39 @@ export default {
 			this.addList.splice(index, 1);
 		},
 		saveHandle() {
-			if (!this.formula) return this.$message.warning('计算公式不能为空');
+			if (!this.formulaList[0].formula) return this.$message.warning('计算公式不能为空');
 			// 指标id数组
       let target_arr = this.addList.filter(item => item.target);
 			
-			this.$emit('ensureBack',{arr:target_arr,formula: this.formula})
+			let formula = JSON.stringify(this.formulaList
+				.filter((_,index) => index===0||(index>0&&_.formula&&_.date))
+				.map(_ => ({f: _.formula,d: _.date}))
+			)
+			
+			this.$emit('ensureBack',{arr:target_arr,formula,...this.nullValueForm})
 			this.cancelHandle();
 		},
 
 		/* 获取数据 */
 		async getResult() {
-			if (!this.formula) return this.$message.warning('计算公式不能为空');
+			if (!this.formulaList[0].formula) return this.$message.warning('计算公式不能为空');
 			
 			let EdbInfoIdArr = this.addList.filter(_ => _.target).map(_ => ({
 				EdbInfoId: _.target,
 				FromTag: _.tag
 			}))
+			const { nullValueWay,maxNullWay } = this.nullValueForm;
+			let formula = JSON.stringify(this.formulaList
+				.filter((_,index) => index===0||(index>0&&_.formula&&_.date))
+				.map(_ => ({f: _.formula,d: _.date}))
+			)
 
 			const params = {
 				RuleType: 9,
 				EndDate: '',
-				Value: this.formula,
+				Value: formula,
+				EmptyType: nullValueWay,
+				MaxEmptyType: maxNullWay,
 				EdbInfoIdArr
 			}
 
@@ -301,6 +444,40 @@ export default {
 				isPredict: _.DataTimestamp > this.$moment(Data.LatestDate+' 8:00').valueOf()
 			})) || [];
 		},
+
+		/* 新增公式分段 */
+		addFormulaHandle() {
+			let addItem = {
+				formula: this.formulaList[this.formulaList.length-1].formula,
+				date: ''
+			}
+			this.formulaList.push(addItem)
+		},
+
+		/* 移除公式 */
+		removeFormulaItem(index) {
+			this.formulaList.splice(index,1)
+		},
+
+		/* 选择日期 检验 */
+		selectFormulaDate(val,item) {
+			const { start_date,end_date } = this.addList[0];
+
+			if(!start_date) return
+
+			let dateStamp = new Date(val).getTime(), 
+					startStamp = new Date(start_date).getTime(),
+					endStamp = new Date(end_date).getTime();
+			if (dateStamp > endStamp || dateStamp < startStamp) {
+				item.date = '';
+				return this.$message.warning('分段日期必须在第一个指标日期区间')
+			}
+
+			else if(this.formulaList.filter(_ => _.date===val).length>1) {
+				item.date = '';
+				return this.$message.warning('分段日期不可重复')
+			}
+		},
 		
 		init() {
 			this.addList = [
@@ -336,6 +513,14 @@ export default {
 			this.searchOptions = [];
 			this.formula = '';
 			this.resultData = [];
+
+			this.formulaList = [
+				{ formula: '' }
+			]
+			this.nullValueForm = {
+				nullValueWay: 0,
+				maxNullWay: 1
+			}
 		},
 		cancelHandle() {
 			this.init();
@@ -395,8 +580,22 @@ export default {
 			margin: 50px 0;
 			.example-txt {
 				display: block;
-				margin-left: 70px;
-				margin-top: 15px;
+				margin-top: 10px;
+			}
+			.computed-section {
+				display: flex;
+				margin-top: 20px;
+			}
+			.label {
+				padding:10px 10px 10px 0;
+			}
+			.formula-item {
+				display: flex;
+				align-items: center;
+				margin-bottom: 15px;
+			}
+			.date-section-text {
+				margin-left: 15px;
 			}
 		}
 	}

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

@@ -384,6 +384,7 @@
 		<!-- 运算指标弹窗 -->
 		<computedDialog
 			:isOpenComputed="computed_type === 31"
+			edbSource="predict"
 			:title="computedTit"
 			:calulateForm="calulateForm"
 			:calulateList="calulateList"
@@ -487,7 +488,8 @@ import edbDetail from './components/edbDetail.vue'
 import edbClassifyDia from './components/classifyDia.vue'
 import changeLang from "@/views/dataEntry_manage/components/changeLang.vue"
 import setEnNameDia from "@/views/dataEntry_manage/components/setEnNameDia.vue"
-import computedDialog from './components/computedDialog';
+// import computedDialog from './components/computedDialog';
+import computedDialog from '@/views/dataEntry_manage/databaseComponents/computedDialog';
 import operationDialog from './components/operationDialog';
 import rulesDetail from './components/rulesDetailDia.vue'
 import batchComputedDialog from '@/views/dataEntry_manage/databaseComponents/batchComptedDialog.vue';
@@ -1170,7 +1172,7 @@ export default {
 		},
 
 		/* 计算指标回显 */
-		setComputedDialogForm({Source,CalculateList,CalculateFormula,EdbInfoId,EdbName,Unit,Frequency,ClassifyId,ClassifyList,MoveType,MoveFrequency,Calendar,CorrelationStr},type='') {
+		setComputedDialogForm({Source,CalculateList,CalculateFormula,EdbInfoId,EdbName,Unit,Frequency,ClassifyId,ClassifyList,MoveType,MoveFrequency,Calendar,CorrelationStr,EmptyType,MaxEmptyType},type='') {
 			//找到指标的父级
 			const parentNodes = ClassifyList.length&&ClassifyList.map(item=>item.ClassifyId)
 			//指标运算 or 其他计算类型指标
@@ -1193,6 +1195,8 @@ export default {
 					targetName: EdbName,
 					unit: Unit,
 					frequency: Frequency,
+					emptyType: EmptyType,
+					maxEmptyType: MaxEmptyType,
 					view: type === 'view'
 				};
 			} else  {

+ 1 - 0
src/views/report_manage/addreportNew.vue

@@ -202,6 +202,7 @@
 						<el-radio :label="3">相关性图表</el-radio>
 						<el-radio :label="6">拟合方程曲线</el-radio>
 						<el-radio :label="7">统计特征</el-radio>
+						<el-radio :label="10">跨品种分析</el-radio>
 					</el-radio-group>
 					<div style="margin: 10px 0">
 						<el-checkbox v-model="isShowMe" @change="() => {search_page=1;$refs.chartListRef.scrollTop = 0;getreportlist(key_word)}">只看我的</el-checkbox>

+ 3 - 0
src/views/report_manage/editChapterReport.vue

@@ -178,6 +178,7 @@
             <el-radio :label="3">相关性图表</el-radio>
             <el-radio :label="6">拟合方程曲线</el-radio>
             <el-radio :label="7">统计特征</el-radio>
+            <el-radio :label="10">跨品种分析</el-radio>
           </el-radio-group>
           <div style="margin: 10px 0">
             <el-checkbox v-model="isShowMe" @change="() => {search_page=1;$refs.chartListRef.scrollTop = 0;getreportlist(key_word)}">只看我的</el-checkbox>
@@ -357,6 +358,8 @@ export default {
       tabsactive: "基础信息",
       ticketList: [], //指标列表
       key_word: "",
+      newreportlist: [],
+
       // 沙盘插入 ----------start
       sandTabelQuery: {
         PageSize: 13,

+ 1 - 0
src/views/report_manage/editreportNew.vue

@@ -202,6 +202,7 @@
 						<el-radio :label="3">相关性图表</el-radio>
 						<el-radio :label="6">拟合方程曲线</el-radio>
 						<el-radio :label="7">统计特征</el-radio>
+						<el-radio :label="10">跨品种分析</el-radio>
 					</el-radio-group>
 					<div style="margin: 10px 0">
 						<el-checkbox v-model="isShowMe" @change="() => {search_page=1;$refs.chartListRef.scrollTop = 0;getreportlist(key_word)}">只看我的</el-checkbox>

+ 47 - 25
src/views/report_manage/mixins/messagePush.js

@@ -1,5 +1,6 @@
 import { reportadd, reportedit, messagePushPost,dataBaseInterface,reportMessageSend } from "@/api/api.js";
 import {approveInterence} from '@/api/modules/approve.js';
+import * as sheetInterface from '@/api/modules/sheetApi.js';
 import { getUrlParams } from '@/utils/common'
 export default {
   data() {
@@ -79,50 +80,71 @@ export default {
       });
     },
 
-    //刷报告中的所有图表  
+    //刷报告中的所有图表和表格
     refreshReport: _.debounce ( async function() {
       let code_arr = [];
+      let sheet_code_arr = []
       $('iframe').each((k,i) => {
         try {
           let href = $(i).attr('src');
-          code_arr.push(getUrlParams(href,'code'));
-    
+          if(href.includes('chartshow')){
+            code_arr.push(getUrlParams(href,'code'));
+          }
+          if(href.includes('sheetshow')){
+            sheet_code_arr.push(getUrlParams(href,'code'))
+          }
         } catch (err) {
         }
       });
 
-      if(!code_arr.length) return this.$message.warning('请插入图表');
+      if(!code_arr.length&&!sheet_code_arr.length) return this.$message.warning('请插入图表或表格')
 
-      if(this.$route.query.id) {
+      const fromPage = this.$route.path === "/reportEnEditor" ? "english_report" : "report";
+      if(this.$route.query.id&&code_arr.length) {
         let res = await dataBaseInterface.getReportrefreshStatus({
-          Source: 'report',
+          Source: fromPage,
           ReportId: Number(this.$route.query.id),
           ReportChapterId: 0
         });
         
         if(!res.Data.RefreshResult) return this.$message.warning('图表正在刷新中,请勿重复操作')
+        const { Ret,Msg } = await dataBaseInterface.reportRefresh({
+            ChartInfoCode: code_arr
+          })
+          
+          if(Ret === 200) {
+            $('iframe').each((k,i) => {
+                let href = $(i).attr('src');
+                if(href.includes('chartshow')){
+                    $(i).attr('src',$(i).attr('src'))
+                }
+            });
+            this.$message.success(Msg);
+          }
       }
 
-      // const loading = this.$loading({
-      //   lock: true,
-      //   text: '刷新中..',
-      //   spinner: 'el-icon-loading',
-      //   background: 'rgba(0, 0, 0, 0.02)'
-      // });
-
-      const { Ret,Msg } = await dataBaseInterface.reportRefresh({
-        ChartInfoCode: code_arr
-      })
-  
-      // loading.close();
-      
-      if(Ret === 200) {
-        $('iframe').each((k,i) => {
-          $(i).attr('src',$(i).attr('src'))
-        });
-        this.$message.success(Msg);
+      if(this.$route.query.id&&sheet_code_arr.length){
+        //获取刷新结果
+        let res = await sheetInterface.getRefreshResult({
+            Source: fromPage,
+            ReportId: Number(this.$route.query.id),
+            ReportChapterId: 0
+          });
+        if(!res.Data.RefreshResult) return this.$message.warning('表格正在刷新中,请勿重复操作')
+        const { Ret,Msg } = await sheetInterface.refreshSheet({
+            ExcelCodes: sheet_code_arr
+          })
+          
+          if(Ret === 200) {
+            $('iframe').each((k,i) => {
+                let href = $(i).attr('src');
+                if(href.includes('sheetshow')){
+                    $(i).attr('src',$(i).attr('src'))
+                }
+            });
+            this.$message.success(Msg);
+          }
       }
-
     },1000),
     //展示提交审批弹窗
     handleSubmitReport(id){

+ 71 - 13
src/views/report_manage/mixins/reportMixin.js

@@ -5,6 +5,7 @@ import chartRelevanceApi from "@/api/modules/chartRelevanceApi";
 import {
   fittingEquationInterface,
   statisticFeatureInterface,
+  crossVarietyInterface
 } from "@/api/modules/chartRelevanceApi";
 export default {
   watch:{
@@ -317,6 +318,7 @@ export default {
           value: chartInfo.ChartName,
           key: "ChartName",
           id: chartInfo.ChartInfoId,
+          source: chartInfo.Source,
           notEdit: true,
         },
         {
@@ -327,7 +329,7 @@ export default {
           placeholder: "请输入英文图表名称",
         }
       );
-      if (![3, 6, 7, 8, 9].includes(chartInfo.Source)) {
+      if ([1,2,5].includes(chartInfo.Source)) {
         this.formItemArray.chartsList = tableData.map((item) => {
           return item.Unit
             ? [
@@ -400,7 +402,7 @@ export default {
       }
 
       //利润曲线
-      if (chartInfo.Source === 5) {
+      else if (chartInfo.Source === 5) {
         this.formItemArray.chartInfo.push(
           {
             label: "盘面利润名称",
@@ -418,6 +420,60 @@ export default {
           }
         );
       }
+
+       //跨品种分析
+      else if(chartInfo.Source===10) {
+        let res = await crossVarietyInterface.chartLangOption({ChartInfoId: chartInfo.ChartInfoId})
+
+        const { TagList,VarietyList } = res.Data;
+
+        this.formItemArray.chartInfo.push({
+          label:'X轴名称',
+          value:Data.DataResp.XName,
+          key:'XName',
+          id:TagList[0].ChartTagId,
+          notEdit:true
+        },
+        {
+          label:'英文X轴名称',
+          value:Data.DataResp.XNameEn,
+          key:'XNameEn',
+          id:TagList[0].ChartTagId,
+          placeholder:'请输入英文X轴名称'
+        },{
+          label:'Y轴名称',
+          value:Data.DataResp.YName,
+          key:'YName',
+          id:TagList[1].ChartTagId,
+          notEdit:true
+        },
+        {
+          label:'英文Y轴名称',
+          value:Data.DataResp.YNameEn,
+          key:'YNameEn',
+          id:TagList[1].ChartTagId,
+          placeholder:'请输入英文Y轴名称'
+        })
+
+        VarietyList.forEach(item => {
+          this.formItemArray.chartsList.push([
+            {
+              label:'品种名称',
+              value:item.ChartVarietyName,
+              key:'ChartVarietyName',
+              id:item.ChartVarietyId,
+              notEdit:true
+            },
+            {
+              label:'英文品种名称',
+              value:item.ChartVarietyNameEn,
+              key:'ChartVarietyNameEn',
+              id:item.ChartVarietyId,
+              placeholder:'请输入英文品种名称'
+            }
+          ])
+        })
+      }
       this.setEnName = true;
     },
 
@@ -452,6 +508,8 @@ export default {
           ChartInfoId: enNameData.ChartInfoId,
           ChartNameEn: enNameData.ChartNameEn,
         });
+      }else if (this.chart_source === 10) {
+        res=await crossVarietyInterface.editChartEn(enNameData)
       }
 
       if (res.Ret !== 200) return;
@@ -471,18 +529,16 @@ export default {
         PageSize: this.search_page_sizes,
         IsShowMe: this.isShowMe,
       };
-      let res = null;
-      if (this.chart_source === 1) {
-        res = await dataBaseInterface.chartSearchByEs(params);
-      } else if (this.chart_source === 2) {
-        res = await futuresInterface.searchChart(params);
-      } else if (this.chart_source === 3) {
-        res = await chartRelevanceApi.searchChart(params);
-      } else if (this.chart_source === 6) {
-        res = await fittingEquationInterface.searchChart(params);
-      } else if (this.chart_source === 7) {
-        res = await statisticFeatureInterface.searchChart(params);
+
+      const apiMap = {
+        1: dataBaseInterface.chartSearchByEs,
+        2: futuresInterface.searchChart,
+        3: chartRelevanceApi.searchChart,
+        6: fittingEquationInterface.searchChart,
+        7: statisticFeatureInterface.searchChart,
+        10: crossVarietyInterface.searchChart,
       }
+      let res = await apiMap[this.chart_source](params)
 
       if (res.Ret !== 200) return;
       res.Data.List = res.Data.List || [];
@@ -491,6 +547,8 @@ export default {
           ? res.Data.List
           : [...this.newreportlist, ...res.Data.List];
       this.search_have_more = this.search_page < res.Data.Paging.Pages;
+
+      console.log(this.chart_source,this.newreportlist)
     },
 
     loadReportHandle() {

+ 1 - 0
src/views/report_manage/reportEn/reportEditor.vue

@@ -212,6 +212,7 @@
 						<el-radio :label="3">相关性图表</el-radio> 
 						<el-radio :label="6">拟合方程曲线</el-radio>
 						<el-radio :label="7">统计特征</el-radio>
+						<el-radio :label="10">跨品种分析</el-radio>
 					</el-radio-group>
 					<div style="margin: 10px 0">
 						<el-checkbox v-model="isShowMe" @change="() => {search_page=1;$refs.chartListRef.scrollTop = 0;getreportlist(key_word)}">只看我的</el-checkbox>

+ 1 - 6
src/views/sandbox_manage/common/edge.js

@@ -4,12 +4,7 @@ const { line} = configOpt;
 import store from "@/vuex/index"
 
 const styleConfig=store.state.sand.styleConfig
-// export const myEdges = [
-// 	{
-// 		type: 'noArrowStraight',
-//     ImgUrl:'~@/assets/icons/arrow.svg'
-// 	}
-// ]
+
 /**type--类型  xP--定位点的横坐标 yP--定位点的纵坐标
  * 创建的线条
  * 斜直线无箭头、斜直线单项箭头、斜直线双线箭头

+ 36 - 78
src/views/sandbox_manage/common/events.js

@@ -5,13 +5,11 @@ const { line} = configOpt;
 
 /* 节点操作监听事件 */
 export const myEvents = (graph,mindmapDataUseFun) => {
-		
 		/* 节点双击编辑 */
 		graph.on('node:dblclick', ({ node, e }) => {
 			// 节点当前设置的样式同步到编辑区
 
 			const { text, rect} = node.attrs;
-			// console.log(node,'nodenode');
 			const edit_area = document.createElement('div');
 			edit_area.contentEditable = "true";
 			edit_area.id = "editable-wrapper";
@@ -23,25 +21,14 @@ export const myEvents = (graph,mindmapDataUseFun) => {
 
 			const position = node.position();
       const size = node.size();
-			// console.log(position,size);
-      // const pos = graph.localToClient(position);
       const pos = graph.localToGraph(position);
       const zoom = graph.zoom();
       let width = size.width,height = size.height;
 
-			// const edit_area =  $('#editable-wrapper')[0];
-			// if(node.shape.indexOf('mindmap')!==-1){
-
-			// 	edit_area.innerText = text.text
-			// 	node.attr('text/text', '');
-			// }else{
-				edit_area.innerText = text.text || text.textWrap.text || ' ';
-				// 开启editText的事务 
-				graph.startBatch('editText')
-				node.attr('text/textWrap/text', '');
-				
-			// }
-
+			edit_area.innerText = text.text || text.textWrap.text || ' ';
+			// 开启editText的事务 
+			graph.startBatch('editText')
+			node.attr('text/textWrap/text', '');
 
 			edit_area.style.left = `${pos.x}px`;
 			edit_area.style.top = `${pos.y}px`;
@@ -68,15 +55,12 @@ export const myEvents = (graph,mindmapDataUseFun) => {
 			edit_area.oninput = () => {
 				const domH = (edit_area.getBoundingClientRect().height) /zoom;
 				node.size(width,domH > 50 ? domH : 50);
-				// node.size(width,domH > height ? domH : height);
-
 			}
 
 			//失焦后设置内容
 			edit_area.onblur = () => {
 				let newval = edit_area.innerText.replace(/(\n[\s\t]*\r*\n)/g,'\n');
 
-				// console.log(node.shape,'edit_area');
 
 				node.attr('text/textWrap/text', newval);
 				node.attr('text/text', newval);
@@ -84,7 +68,8 @@ export const myEvents = (graph,mindmapDataUseFun) => {
 				graph.stopBatch('editText')
 
 				if(node.shape.indexOf('mindmap')!==-1){
-					const mindmapDataUse=mindmapDataUseFun()
+					if(!mindmapDataUseFun) return 
+					const mindmapDataUse=mindmapDataUseFun().mindmapDataUse
 					let ids = node.id.split('-')
 					let mindmapDataIndex = mindmapDataUse.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
 					let mindMapDataCurrent = mindmapDataUse[mindmapDataIndex]?mindmapDataUse[mindmapDataIndex].mindmapData:{}
@@ -97,23 +82,15 @@ export const myEvents = (graph,mindmapDataUseFun) => {
 					}
 					mindMapDataCurrent.label = newval
 				}
-				if(node.shape.indexOf('mindmap')==-1){
-					const domH = (edit_area.getBoundingClientRect().height) /zoom;
-					node.size(width,domH > 50 ? domH : 50);
-					// node.size(width,domH);
-				}else{
-					const domH = (edit_area.getBoundingClientRect().height) /zoom;
-					// node.size(width,domH);
-					node.size(width,domH > 50 ? domH : 50);
-					// node.fit({ deep: true })
-				}
+
+				const domH = (edit_area.getBoundingClientRect().height) /zoom;
+				node.size(width,domH > 50 ? domH : 50);
 
 				$('#sand-chart-container')[0].removeChild(edit_area);
 			}
 		})
 		/* 鼠标移入移出控制连接桩 */
 		graph.on('node:mouseenter', ({ node, e }) => {
-			// console.log(node)
 			for(let i of document.querySelectorAll(`g[data-cell-id="${node.id}"] .x6-port-body`)) {
 				i.style.display = 'block'
 			}
@@ -135,7 +112,6 @@ export const myEvents = (graph,mindmapDataUseFun) => {
 
 		/* 选中事件 */
 		// graph.on('cell:selected',({cell,options}) => {
-		// 	// console.log(cell)
 		// 	//节点
 		// 	if(cell.shape === 'rect') {
 		// 		const { key } = cell.data;
@@ -174,21 +150,16 @@ export const myEvents = (graph,mindmapDataUseFun) => {
 		// })
 
 		const changeSelection=_.debounce((selected)=> {
-			// console.log(selected,'selected');
 			store.commit('sand/SET_SELECT_CELLS',selected)
 		},50)
 
 		/* 监听选中事件 */
 		graph.on('selection:changed',({selected}) => {
-			// console.log(selected,'选中修改');
-			// selected.length ? store.commit('sand/SET_SELECT_CELL',selected[0]) : store.commit('sand/SET_SELECT_CELL',null);
 			changeSelection(selected)
 		})
 
 		/* 监听选中事件 */
 		graph.on('edge:mouseenter', ({ cell }) => {
-			// console.log(cell,'myEdgeOption');
-			// console.log(cell.store.data);
 			if(cell.store.data.shape=="mindmap-edge"){
 				// 思维导图的边
 			}else{
@@ -236,11 +207,10 @@ export const myEvents = (graph,mindmapDataUseFun) => {
 }
 
 /* 绑定键盘事件 */
-export const bindKey = (graph,mindmapDataUseFun,mindmapAssistData) => {
+export const bindKey = (graph,mindmapDataUseFun) => {
 	 // 删除
 	graph.bindKey(['delete', 'backspace'], () => {
-		const mindmapDataUse=mindmapDataUseFun()
-		deleteNodes(graph,mindmapDataUse,mindmapAssistData)
+		deleteNodes(graph,mindmapDataUseFun)
 		return false
 	}, 'keydown');
 
@@ -278,27 +248,20 @@ export const bindKey = (graph,mindmapDataUseFun,mindmapAssistData) => {
 }
 
 /* 右键事件 */
-export const contextEvent = (graph,key,mindmapDataUse,mindmapAssistData) => {
-	// console.log(key)
+export const contextEvent = (graph,key,mindmapDataUseFun) => {
 	switch (key) {
 		case 'copy':
 			nodeCopyAndPaste(graph);
 			break;
 		case 'del': 
-			nodeDelete(graph,mindmapDataUse,mindmapAssistData);
+			deleteNodes(graph,mindmapDataUseFun);
 			break;
 	}
 }
 
-/* 删除节点 清空选区*/
-const nodeDelete = (graph,mindmapDataUse,mindmapAssistData) => {
-	deleteNodes(graph,mindmapDataUse,mindmapAssistData)
-}
 /* 复制粘贴节点 */
 const nodeCopyAndPaste = (graph) => {
 	const select_cell = graph.getSelectedCells();
-	// console.log(select_cell)
-	// return
 	if (select_cell.length) {
 		graph.copy(select_cell);
 		const copy_cell = graph.paste({ offset: 30 });
@@ -309,7 +272,6 @@ const nodeCopyAndPaste = (graph) => {
 
 /* 关联三种基础图形选中样式 */
 // const setSelectedOptions = ({ rect, text }, key) => {
-// 	console.log({ rect, text },'{ rect, text }',key);
 // 	return ['rect','date'].includes(key) ? {
 // 		text: { //文本设置
 // 			size: text.fontSize,
@@ -332,13 +294,19 @@ const nodeCopyAndPaste = (graph) => {
 // 	}: {}
 // }
 
-const deleteNodes=(graph,mindmapDataUse,mindmapAssistData)=>{
-	console.log(mindmapDataUse,mindmapAssistData,'最源数据');
+const deleteNodes=(graph,mindmapDataUseFun)=>{
 	const select_cell = graph.getSelectedCells();
 	let delete_cells=[]
+	if(!mindmapDataUseFun){
+		delete_cells = select_cell
+		if (delete_cells.length) {
+			// 移除工具
+			delete_cells.forEach(item => item.removeTools());
+			graph.removeCells(delete_cells)
+		}
+		return 
+	}
 	let mindmapCell=[]
-	// return 
-	// console.log(select_cell.length,'select_cell.length');
 	for (let i = 0; i < select_cell.length; i++) {
 		const cell = select_cell[i];
 		if(cell.shape == "mindmap-edge"){
@@ -347,63 +315,53 @@ const deleteNodes=(graph,mindmapDataUse,mindmapAssistData)=>{
 			mindmapCell.push(cell)
 			// 拿到该节点的所有子节点
 			let Successors = graph.getSuccessors(cell) 
-			// console.log(Successors,'Successors');
 			mindmapCell = [...mindmapCell,...Successors]
 		}
 		delete_cells.push(cell)
 	}
 	// 去重
 	const uniqueArr = mindmapCell.filter((item, index) => mindmapCell.findIndex(i => i.id === item.id) === index);
-	// console.log(uniqueArr,'uniqueArr');
 
 	delete_cells = [...delete_cells,...uniqueArr]
-	// return 
 	if (delete_cells.length) {
 		// 移除工具
 		delete_cells.forEach(item => item.removeTools());
 		graph.removeCells(delete_cells)
 	}
+	const mindmapAssistData = mindmapDataUseFun()
+
 	let shouldOperations=[]
-	mindmapDataUse.map((item,index)=>{
+	mindmapAssistData.mindmapDataUse.map((item,index)=>{
 		let levelIds = uniqueArr.filter(mindMap => mindMap.id.startsWith(item.mindmapData.id)).map(mindMap => mindMap.id)
-		// console.log(levelIds,'levelIds');
 		if(!(levelIds && levelIds.length>0)) return 
-		// console.log(levelIds,'levelIds');
 		let mindMapIds=[...levelIds]
 		for (let i = 0; i < levelIds.length; i++) {
 			const element = levelIds[i]
 			mindMapIds=mindMapIds.filter( id => id.indexOf(element) !=0 || id==element)
 		}
-		// console.log(mindMapIds,'mindMapIds');
 		shouldOperations.push(mindMapIds)
 	})
-	// 删除前备份
-	console.log(mindmapDataUse,'元数据');
-	mindmapAssistData.mindmapDataRecoverUse=[JSON.stringify(mindmapDataUse)]
 
-	console.log('删除备份',JSON.parse(mindmapAssistData.mindmapDataRecoverUse[0]),mindmapAssistData.mindmapDataRecoverUse.length,mindmapAssistData.mindmapDataRecoverUse);
-	
-	// console.log(shouldOperations,'shouldOperations');
+	mindmapAssistData.deletedMindmapData.push([])
 	shouldOperations.map(it =>{
 		it.map(it1 =>{
-			deleteMindmapData(it1,mindmapDataUse)
+			deleteMindmapData(it1,mindmapAssistData)
 		})
 	})
-	// console.log(mindmapDataUse,'mindmapDataUse',mindmapAssistData,'mindmapAssistData');
-
 }
 
 const deleteMindmapData=(id,data)=>{
-	// console.log(id,data,'id,data');
-	// return 
+
 	let ids = id.split('-')
-	let mindmapDataIndex = data.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
+	let mindmapDataIndex = data.mindmapDataUse.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
 	if(ids.length==1){
-		data.splice(mindmapDataIndex,1)
+		let spliceData=data.mindmapDataUse.splice(mindmapDataIndex,1)
+		// 备份删除掉的数据
+		data.deletedMindmapData[data.deletedMindmapData.length-1].push(spliceData)
 		return 
 	}
 
-	let mindmapData = data[mindmapDataIndex].mindmapData
+	let mindmapData = data.mindmapDataUse[mindmapDataIndex].mindmapData
 	let findId = ids[0]
 	for (let i = 1; i < ids.length-1; i++) {
 		const element = ids[i];
@@ -418,6 +376,6 @@ const deleteMindmapData=(id,data)=>{
 		return 
 	}
 	let endIndex = mindmapData.children.findIndex(it => it.id == findId+'-'+endId)
-	mindmapData.children.splice(endIndex,1)
-	// console.log(data);
+	let spliceData=mindmapData.children.splice(endIndex,1)
+	data.deletedMindmapData[data.deletedMindmapData.length-1].push(spliceData)
 }

+ 3 - 5
src/views/sandbox_manage/common/gragh.js

@@ -2,12 +2,11 @@ import { Graph,Shape } from '@antv/x6';
 import { bindKey,myEvents } from './events';
 import { configOpt } from './toolConfig';
 import store from '@/vuex/index'
-console.log(store,'store');
 const styleConfig=store.state.sand.styleConfig
 
 const { line } = configOpt;
 // wrapper DOM的Id mindmapDataUseFun 返回思维导图数组函数,传递给事件 type 模式,编辑和查看
-export function myGraph (wrapper,mindmapDataUseFun,mindmapAssistData,type='edit') {
+export function myGraph (wrapper,mindmapDataUseFun,type='edit') {
 	const graph = new Graph({
 		container: document.getElementById(wrapper),
 		// width: $(window).width(),
@@ -15,7 +14,6 @@ export function myGraph (wrapper,mindmapDataUseFun,mindmapAssistData,type='edit'
 		history:{
 			enabled:true,
 			beforeAddCommand(event, args){
-				console.log(event, args,'event, args');
 				if(args.key=='tools'){
 					// 工具的改变不加入撤销和重做的队列
 					return false
@@ -61,7 +59,7 @@ export function myGraph (wrapper,mindmapDataUseFun,mindmapAssistData,type='edit'
 			visible: false,
 		},
 		highlighting: {
-			// 当链接桩可以被链接时,在链接桩外围渲染一个 2px 宽的色矩形框
+			// 当链接桩可以被链接时,在链接桩外围渲染一个 2px 宽的天蓝色矩形框
 			magnetAvailable: {
 					name: "stroke",
 					args: {
@@ -150,7 +148,7 @@ export function myGraph (wrapper,mindmapDataUseFun,mindmapAssistData,type='edit'
 	
 
 	/* 键盘事件 */
-	if(type!='view') bindKey(graph,mindmapDataUseFun,mindmapAssistData);
+	if(type!='view') bindKey(graph,mindmapDataUseFun);
 
 	return graph;
 }

+ 223 - 108
src/views/sandbox_manage/common/mindmap.js

@@ -7,7 +7,12 @@ export default {
       mindMapDataCurrent:{},
       positionCurrent:{},
       addTypeCurrent:'',
-      styleConfig:{}
+      styleConfig:{},
+      mindmapAssistData:{
+        mindmapDataUse:[],
+        deletedMindmapData:[],// 用于撤销删除,恢复删除掉的数据
+        mindmapDataRecoverUse:[],//用于 重做时 恢复思维导图数据
+      },
     }
   },
   mounted() {
@@ -15,7 +20,6 @@ export default {
   },
   created() {
     this.styleConfig=this.$store.state.sand.styleConfig
-    // console.log(this.styleConfig,'this.styleConfig');
     // 中心主题
     Graph.registerNode(
       'mindmap-topic',          
@@ -43,8 +47,6 @@ export default {
           body: {
             rx: 6,
             ry: 6,
-            // stroke: this.$store.state.sand.styleConfig.borderColor,
-            // fill: this.$store.state.sand.styleConfig.backgroundColor,
             strokeWidth: 1,
             width:100,
             height:50
@@ -111,8 +113,6 @@ export default {
           body: {
             rx: 6,
             ry: 6,
-            // stroke: this.$store.state.sand.styleConfig.borderColor,
-            // fill: this.$store.state.sand.styleConfig.backgroundColor,
             strokeWidth: 1,
             width:100,
             height:50
@@ -167,8 +167,6 @@ export default {
           body: {
             rx: 6,
             ry: 6,
-            // stroke: this.$store.state.sand.styleConfig.borderColor,
-            // fill: this.$store.state.sand.styleConfig.backgroundColor,
             strokeWidth: 1,
             width:100,
             height:50
@@ -196,53 +194,12 @@ export default {
       },
       true,
     )
-    // 分支主题
-    // Graph.registerNode(
-    //   'mindmap-topic-child',
-    //   {
-    //     inherit: 'rect',
-    //     markup: [
-    //       {
-    //         tagName: 'rect',
-    //         selector: 'body',
-    //       },
-    //       {
-    //         tagName: 'text',
-    //         selector: 'text',
-    //       },
-    //       {
-    //         tagName: 'path',
-    //         selector: 'line',
-    //       },
-    //     ],
-    //     attrs: {
-    //       body: {
-    //         fill: '#ffffff',
-    //         strokeWidth: 0,
-    //         stroke: '#5F95FF',
-    //       },
-    //       text: {
-    //         fontSize: 14,
-    //         fill: '#262626',
-    //         textVerticalAnchor: 'bottom',
-    //       },
-    //       line: {
-    //         stroke: '#5F95FF',
-    //         strokeWidth: 2,
-    //         d: 'M 0 15 L 60 15',
-    //       },
-    //     },
-    //   },
-    //   true,
-    // )
-
     // 连接器
     Graph.registerConnector(
       'mindmap',
       (sourcePoint, targetPoint, routerPoints, options) => {
-        // console.log(sourcePoint, targetPoint, routerPoints, options,'sourcePoint, targetPoint, routerPoints, options');
         const midX = sourcePoint.x + 10
-        const midY = sourcePoint.y
+        // const midY = sourcePoint.y
         const ctrX = (targetPoint.x - midX) / 5 + midX
         const ctrY = targetPoint.y
         const pathData = `
@@ -265,7 +222,6 @@ export default {
         attrs: {
           line: {
             targetMarker: false,
-            // stroke: this.$store.state.sand.styleConfig.lineColor,
             strokeWidth: 2,
           },
         },
@@ -276,6 +232,7 @@ export default {
   },
   methods: {
     setGraph(){
+      // 添加左边节点
       this.graph.on('add:topic:left', ({ node }) => {
         if(this.operationType=='view') return 
         const { id } = node
@@ -285,7 +242,7 @@ export default {
           this.mindMapRender()      
         }
       })
-
+      // 添加右边节点
       this.graph.on('add:topic:right', ({ node }) => {
         if(this.operationType=='view') return 
         const { id } = node
@@ -295,12 +252,13 @@ export default {
           this.mindMapRender()      
         }
       })
+      // 改变大小 - 更新数据源
       this.graph.on('node:change:size', (args) => { 
         if(this.operationType=='view') return 
         if(args.node.shape.indexOf('mindmap')!==-1){
           let ids = args.node.id.split('-')
-					let mindmapDataIndex = this.mindmapDataUse.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
-					let mindMapDataCurrent = this.mindmapDataUse[mindmapDataIndex]?this.mindmapDataUse[mindmapDataIndex].mindmapData:{}
+					let mindmapDataIndex = this.mindmapAssistData.mindmapDataUse.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
+					let mindMapDataCurrent = this.mindmapAssistData.mindmapDataUse[mindmapDataIndex]?this.mindmapAssistData.mindmapDataUse[mindmapDataIndex].mindmapData:{}
 
 					let findId = ids[0]
 
@@ -314,39 +272,19 @@ export default {
 					mindMapDataCurrent.height = args.node.size().height
         }
       })
+      // 改变位置 - 更新数据源
       this.graph.on('node:change:position', (args) => { 
         if(this.operationType=='view') return 
         if(args.node.shape.indexOf('mindmap')!==-1 && Number(args.node.id)){
 
-          let index = this.mindmapDataUse.findIndex(it =>it.mindmapData.id == args.node.id)
-          // let index = Number(args.node.id)
+          let index = this.mindmapAssistData.mindmapDataUse.findIndex(it =>it.mindmapData.id == args.node.id)
           if(index!=-1){
-            this.mindmapDataUse[index].position = args.current
-          // this.setCurrent(args.node.id)
+            this.mindmapAssistData.mindmapDataUse[index].position = args.current
           }
         }
       })
-      // this.graph.on('node:added', ({node}) => { 
-      //   if(node.shape.indexOf('mindmap')!=-1){
-      //     // 思维导图需要处理相对应的数据
-      //     console.log('mindmap',node.id);
-      //   }
-      //   console.log(node,'node:added');
-      // })
-      // this.graph.on('node:removed', (args) => { 
-      //   console.log(args,'node:removed');
-      // })
-      // this.graph.bindKey(['backspace', 'delete'], () => {
-      //   const selectedNodes = this.graph.getSelectedCells().filter((item) => item.isNode())
-      //   if (selectedNodes.length) {
-      //     const { id } = selectedNodes[0]
-      //     if (this.removeNode(id)) {
-      //       this.mindMapRender()
-      //     }
-      //   }
-      // })
-      
-      this.graph.bindKey('tab', (e) => {  
+      // tab 键添加节点-右边的 - 更新数据源
+      this.graph.bindKey('tab', (e) => {
         if(this.operationType=='view') return 
         e.preventDefault()
         const selectedNodes = this.graph.getSelectedCells().filter((item) => {
@@ -362,20 +300,88 @@ export default {
           }
         }
       })
+      // 撤销 - 入栈
+      this.graph.history.on('undo', (args) => { 
+        let mindmapNodes=args.cmds.filter(it => it.data.props && it.data.props.shape.indexOf('mindmap')!=-1 && it.data.node)
+        if(!(mindmapNodes && mindmapNodes.length>0)) return 
+        let mindmapUndoType=mindmapNodes[0].event
+        
+        if(mindmapUndoType=="cell:added"){
+          this.mindmapDeleteRecordPush(mindmapNodes)
+        }else if(mindmapUndoType=="cell:removed"){
+          this.mindmapAddRecordPush()
+        }
+      })
+      // 重做,出栈
+      this.graph.history.on('redo', (args) => { 
+        let mindmapNodes=args.cmds.filter(it => it.data.props && it.data.props.shape.indexOf('mindmap')!=-1 && it.data.node)
+        if(!(mindmapNodes && mindmapNodes.length>0)) return
+        this.mindmapRecover()
+      })
+    },
+    // 生成思维导图初始化数据
+    generateMindmapData(position,addType){
+      let beId=this.mindmapAssistData.mindmapDataUse.length>0?
+      parseInt(this.mindmapAssistData.mindmapDataUse[this.mindmapAssistData.mindmapDataUse.length-1].mindmapData.id)+1+'':'1'
+      let mindmapData={
+        id: beId,
+        type: 'topic',
+        label: '中心主题',
+        width: 160,
+        height: 50,
+        direction:'double',
+        children: [
+          {
+            id: beId+'-1',
+            type: 'topic-branch',
+            label: '分支主题1',
+            width: 100,
+            height: 40,
+            direction:'left',
+            children: [
+              {
+                id: beId+'-1-1',
+                type: 'topic-child',
+                label: '子主题1',
+                width: 60,
+                height: 30,
+                direction:'left',
+              },
+              {
+                id: beId+'-1-2',
+                type: 'topic-child',
+                label: '子主题2',
+                width: 60,
+                height: 30,
+                direction:'left',
+              },
+            ],
+          },
+          {
+            id: beId+'-2',
+            type: 'topic-branch',
+            label: '分支主题2',
+            width: 100,
+            height: 40,
+            direction:'right',
+          },
+        ],
+      }
+      this.mindmapAssistData.mindmapDataUse.push({mindmapData,position,addType})
     },
     // 通过id设置当前操作的思维导图
     setCurrent(id){
       let rootId = id.split('-')[0]
-      let index = this.mindmapDataUse.findIndex(it =>it.mindmapData.id == rootId)
-      this.mindMapDataCurrent = this.mindmapDataUse[index]?this.mindmapDataUse[index].mindmapData:{}
-      this.positionCurrent = this.mindmapDataUse[index]?this.mindmapDataUse[index].position:{x:0,y:0}
-      this.addTypeCurrent = this.mindmapDataUse[index]?this.mindmapDataUse[index].addType:'singleMindmap'
+      let index = this.mindmapAssistData.mindmapDataUse.findIndex(it =>it.mindmapData.id == rootId)
+      this.mindMapDataCurrent = this.mindmapAssistData.mindmapDataUse[index]?this.mindmapAssistData.mindmapDataUse[index].mindmapData:{}
+      this.positionCurrent = this.mindmapAssistData.mindmapDataUse[index]?this.mindmapAssistData.mindmapDataUse[index].position:{x:0,y:0}
+      this.addTypeCurrent = this.mindmapAssistData.mindmapDataUse[index]?this.mindmapAssistData.mindmapDataUse[index].addType:'singleMindmap'
     },
     mindMapRender(i){
       this.graph.startBatch('renderMindmap')
-      let mindMapType = i || i==0?this.mindmapDataUse[i].addType:this.addTypeCurrent
-      this.mindMapDataCurrent = i || i==0?this.mindmapDataUse[i].mindmapData:this.mindMapDataCurrent
-      this.positionCurrent = i || i==0?this.mindmapDataUse[i].position:this.positionCurrent
+      let mindMapType = i || i==0?this.mindmapAssistData.mindmapDataUse[i].addType:this.addTypeCurrent
+      this.mindMapDataCurrent = i || i==0?this.mindmapAssistData.mindmapDataUse[i].mindmapData:this.mindMapDataCurrent
+      this.positionCurrent = i || i==0?this.mindmapAssistData.mindmapDataUse[i].position:this.positionCurrent
       const result = Hierarchy.mindmap(this.mindMapDataCurrent, {
         direction: 'H',
         getHeight(d) {
@@ -397,14 +403,11 @@ export default {
       const cells = []
       let xGap = this.positionCurrent?this.positionCurrent.x-result.x:0
       let yGap = this.positionCurrent?this.positionCurrent.y-result.y:0
-      // console.log(result,'result');
-      // return 
+
       const traverse = (hierarchyItem) => {
         if (hierarchyItem) {
           const { data, children } = hierarchyItem
-          // console.log(hierarchyItem,'hierarchyItemhierarchyItemhierarchyItem');
           let mindmapDirection = mindMapType.indexOf('double') != -1?data.direction:'right'
-          // console.log(mindmapDirection,data.label,data.direction);
           let currentCell=this.graph.getCellById(data.id)
 
           if(!currentCell){
@@ -434,7 +437,6 @@ export default {
             // 有,更新下位置信息
             currentCell.position(xGap+hierarchyItem.x,yGap+hierarchyItem.y)
           }
-          // return 
    
           if (children) {
             children.forEach((item) => {
@@ -477,13 +479,10 @@ export default {
       traverse(result)
       // 排下序,把边放最后面 不然 边 会找不到 节点
       let sortCells = cells.sort(cell => cell.shape.indexOf('edge'))
-      // console.log(sortCells,'result');
-      // return 
-      // this.graph.removeCells(sortCells)
+
       this.graph.addCell(sortCells)
       this.graph.stopBatch('renderMindmap')
-      // this.graph.resetCells(cells)
-      // this.graph.centerContent()
+
     },
     findItem(obj,id){
       if (obj.id === id) {
@@ -507,16 +506,13 @@ export default {
       return null
     },
     addChildNode (id, type,direction='left'){
-      console.log('添加');
       // 重做不了 清空重做栈
       this.mindmapAssistData.mindmapDataRecoverUse = []
-      // console.log(this.mindmapAssistData);
 
       const res = this.findItem(this.mindMapDataCurrent, id)
       const dataItem = res && res.node
       if (dataItem) {
         let item = null
-        // console.log(dataItem.children);
         let addId ='1'
         if(dataItem.children && dataItem.children.length>0){
           let ids = dataItem.children[dataItem.children.length-1].id.split('-')
@@ -552,15 +548,134 @@ export default {
       }
       return null
     },
-    removeNode (id) {
-      const res = this.findItem(this.mindMapDataCurrent, id)
-      const dataItem = res && res.parent
-      if (dataItem && dataItem.children) {
-        const { children } = dataItem
-        const index = children.findIndex((item) => item.id === id)
-        return children.splice(index, 1)
+    getMindmapDataUse(){
+      return this.mindmapAssistData
+    },
+    mindmapDeleteRecordPush(mindmapNodes){
+      let shouldOperations=[]
+      this.mindmapAssistData.mindmapDataUse.map((item,index)=>{
+        let levelIds = mindmapNodes.filter(mindMap => mindMap.data.id.startsWith(item.mindmapData.id)).map(mindMap => mindMap.data.id)
+        if(!(levelIds && levelIds.length>0)) return 
+        let mindMapIds=[...levelIds]
+        for (let i = 0; i < levelIds.length; i++) {
+          const element = levelIds[i]
+          mindMapIds=mindMapIds.filter( id => id.indexOf(element) !=0 || id==element)
+        }
+        shouldOperations.push(mindMapIds)
+      })
+      // 入栈
+      if(!this.canRedo){
+        this.mindmapAssistData.mindmapDataRecoverUse=[JSON.stringify(this.mindmapAssistData.mindmapDataUse)]
+      }else{
+        this.mindmapAssistData.mindmapDataRecoverUse.push(JSON.stringify(this.mindmapAssistData.mindmapDataUse))
       }
-      return null
-    }
+      shouldOperations.map(it =>{
+        it.map(it1 =>{
+          this.deleteMindmapData(it1,this.mindmapAssistData.mindmapDataUse)
+        })
+      })
+    },
+    mindmapAddRecordPush(){
+      // 入栈
+      if(!this.canRedo){
+        this.mindmapAssistData.mindmapDataRecoverUse=[JSON.stringify(this.mindmapAssistData.mindmapDataUse)]
+      }else{
+        this.mindmapAssistData.mindmapDataRecoverUse.push(JSON.stringify(this.mindmapAssistData.mindmapDataUse))
+      }
+      let recoverDeletedData=this.mindmapAssistData.deletedMindmapData.pop()
+      if(recoverDeletedData && recoverDeletedData.length>0){
+        // 添加
+        recoverDeletedData.map(it =>{
+          it.map(it1 =>{
+            this.addMindmapData(it1,this.mindmapAssistData.mindmapDataUse)
+          })
+        })
+      }
+    },
+    deleteMindmapData(id,data){
+      let ids = id.split('-')
+      let mindmapDataIndex = data.findIndex(mindmap => mindmap.mindmapData.id == ids[0])
+      if(ids.length==1){
+        data.splice(mindmapDataIndex,1)
+        return 
+      }
+
+      let mindmapData = data[mindmapDataIndex].mindmapData
+      let findId = ids[0]
+      for (let i = 1; i < ids.length-1; i++) {
+        const element = ids[i];
+        findId = findId+'-'+element
+        mindmapData=mindmapData.children.find(it => it.id==findId)
+      }
+      let endId = ids[ids.length-1]
+      let endIndex = mindmapData.children.findIndex(it => it.id == findId+'-'+endId)
+      mindmapData.children.splice(endIndex,1)
+    },
+    addMindmapData(item,data){
+      let isRoot = item.mindmapData?true:false
+      let id = isRoot?item.mindmapData.id:item.id
+      
+      if(isRoot){
+        // 恢复根节点
+        let mindmapDataIndexRoot=data.length
+        for (let i = 0; i < data.length-1; i++) {
+          const element = data[i];
+          if((+id) < (+element.mindmapData.id)){
+            mindmapDataIndexRoot=0
+            break
+          }else if((+id) > (+element.mindmapData.id) && (+id) < (+data[i+1].mindmapData.id)){
+            mindmapDataIndexRoot=i+1
+            break
+          }
+        }
+        data.splice(mindmapDataIndexRoot,0,item)
+        return
+      }
+
+      let ids = id.split('-')
+      let mindmapDataIndex = data.findIndex(mindmap => {
+        return mindmap.mindmapData.id == ids[0]
+      })
+      let mindmapData = data[mindmapDataIndex].mindmapData
+      let findId = ids[0]
+      for (let i = 1; i < ids.length-1; i++) {
+        const element = ids[i];
+        findId = findId+'-'+element
+        mindmapData=mindmapData.children.find(it => it.id==findId)
+      }
+      let endId = ids[ids.length-1]
+      let endIndex = mindmapData.children.length
+      if(mindmapData.children.length==1){
+        // 只有一个
+        let prevIdx = mindmapData.children[0].id.substring(mindmapData.children[0].id.lastIndexOf('-')+1)
+        if((+endId) < (+prevIdx)){
+          endIndex=0
+        }else{
+          endIndex=1
+        }
+      }else{
+        for (let i = 0; i < mindmapData.children.length-1; i++) {
+          // 两个及以上
+          const element = mindmapData.children[i];
+
+          let prevIdx = element.id.substring(element.id.lastIndexOf('-')+1)
+          let nextIdx = mindmapData.children[i+1].id.substring(mindmapData.children[i+1].id.lastIndexOf('-')+1)
+          // 找到对应位置,前提 id是按顺序的
+          if((+endId) < (+prevIdx)){
+            endIndex=0
+            break
+          }else if((+endId) > (+prevIdx) && (+endId) < (+nextIdx)){
+            endIndex=i+1
+            break
+          }
+        }
+      }
+      mindmapData.children.splice(endIndex,0,item)
+    },
+    mindmapRecover(){
+      // 重做添加时 恢复数据指针前进
+      let recoverData = this.mindmapAssistData.mindmapDataRecoverUse.pop()
+      if(recoverData) this.mindmapAssistData.mindmapDataUse=JSON.parse(recoverData) 
+    },
   },
 }

+ 1 - 12
src/views/sandbox_manage/common/node.js

@@ -3,7 +3,6 @@ import store from "@/vuex/index"
 
 const { line,border,text } = configOpt;
 
-console.log(store,'storestorestore');
 const styleConfig=store.state.sand.styleConfig
 
 //定义图形
@@ -273,17 +272,7 @@ export const myNodeOption = (key) => {
 							// 		position: 'bottom',
 							// 		zIndex: 20,
 							// 		...portStyle
-							// },
-							// "port-left": {
-							// 		position: 'left',
-							// 		zIndex: 20,
-							// 		...portStyle
-							// },
-							// "port-right": {
-							// 		position: 'right',
-							// 		zIndex: 20,
-							// 		...portStyle
-							// },
+							// }
 					}
 				},
 			}

+ 158 - 274
src/views/sandbox_manage/index_new_version.vue

@@ -110,6 +110,7 @@
     </div>
     <!-- 目录自定义按钮区域 -->
     <div id="custom-button-zone" class="custom-button-zone">
+      <img src="~@/assets/img/data_m/move_ico.png" style="pointer-events: none;" v-permission="permissionBtn.sandboxPermission.sandbox_classify_move"/>
       <img src="~@/assets/img/sand_new/add_outline.png" class="add-classify-img" v-permission="permissionBtn.sandboxPermission.sandbox_classify_addEdit"/>
       <img src="~@/assets/img/sand_new/edit_outline.png" class="edit-classify-img" v-permission="permissionBtn.sandboxPermission.sandbox_classify_addEdit"/>
       <img src="~@/assets/img/sand_new/delete_outline.png" class="delete-classify-img" v-permission="permissionBtn.sandboxPermission.sandbox_classify_del"/>
@@ -129,15 +130,18 @@
             {{ lastLevelClassifyName }}
           </el-form-item>
           <el-form-item label="目录名称" prop="SandboxClassifyName">
-            <el-input v-model="classifyForm.SandboxClassifyName" style="width: 317px;" placeholder="请输入目录名称"></el-input>
+            <el-input v-model.trim="classifyForm.SandboxClassifyName" style="width: 317px;" placeholder="请输入目录名称"></el-input>
           </el-form-item>
-          <el-form-item label="关联品种" prop="ChartPermissionId" v-if="hasVariety && (classifyAddTitle.indexOf('添加')!==-1)">
+          <el-form-item label="关联品种" prop="ChartPermissionId" v-if="hasVariety">
             <el-cascader 
+              @change="chartPermissionIdChange"
 							:options="classifyArr"
 							:props="classifyProps"
 							v-model="classifyForm.ChartPermissionId" 
 							placeholder="请选择对应品种" 
               id="classifyAddCascader"
+              ref="chartPermissionCascader"
+              :disabled="(classifyForm.SandboxClassifyId && classifyForm.Level!=1) || (!classifyForm.SandboxClassifyId && classifyForm.Level!=0)"
 						/>
           </el-form-item>
         </el-form>
@@ -162,24 +166,15 @@
             <el-input v-model="saveOtherForm.chartName" style="width: 317px;" placeholder="请输入逻辑图名称"></el-input>
           </el-form-item>
           <el-form-item label="分类" prop="classifyId" :rules="{required:true,message:'请选择分类',trigger:'change'}" >
-            <el-popover
-              placement="bottom"
-              width="400"
-              popper-class="classify-popper"
-              trigger="click"
-              v-model="selectClassifyShow">
-                <tree :nodes="onlyClassifyTreeData" :setting="selectSetting" key="saveOther" @onCreated="getSelectZTree" @onClick="selectClassify" />
-                <el-cascader 
-                slot="reference"
-                :options="onlyClassifyTreeData"
-                :props="{children: 'Children',
-                  label: 'SandboxClassifyName',
-                   value: 'SandboxClassifyId',emitPath:false,checkStrictly:true}"
-                v-model="saveOtherForm.classifyId" 
-                popper-class="classify-cascader-popper"
-                placeholder="请选择分类">
-                </el-cascader>
-            </el-popover>
+            <el-cascader 
+            :options="onlyClassifyTreeData"
+            @change="classifyTreeChange"
+            :props="{children: 'Children',
+              label: 'SandboxClassifyName',
+                value: 'SandboxClassifyId',checkStrictly:true}"
+            v-model="saveOtherClassifys" 
+            placeholder="请选择分类">
+            </el-cascader>
           </el-form-item>
         </el-form>
         <div style="text-align: center;padding:40px 0 ;">
@@ -210,7 +205,6 @@ import { myGraph } from './common/gragh';
     mixins:[mindmap],
     watch: {
       initData(newval) {
-        console.log(newval);
         if(!this.graph){
           this.$nextTick(()=>{
             this.init()
@@ -261,9 +255,7 @@ import { myGraph } from './common/gragh';
             showLine:false,
             showIcon:false,
             selectedMulti:false,
-            addDiyDom: this.addDiyDom,
-            // addHoverDom: this.addHoverDom,
-		        // removeHoverDom: this.removeHoverDom
+            addHoverDom: this.addHoverDom,
           },
           edit:{
             enable:true,
@@ -283,25 +275,11 @@ import { myGraph } from './common/gragh';
           }
         },
         zTreeObj:{},
-        selectSetting:{
-          data:{
-            key:{
-              name:"SandboxClassifyName",
-              children:"Children"
-            }
-          },
-          view:{
-            showLine:false,
-            showIcon:false,
-            selectedMulti:false
-          }
-        },
-        selectClassifyShow:false,
-        selectZTreeObj:{},
         lockLoding:null,
         viewSandbox:{},
         activeNode:{},
         addClassifyNodeTid:'',
+        locationNode:'',
         movingRecoveData:{
           targetNode:null,
           treeNode:null,
@@ -309,6 +287,7 @@ import { myGraph } from './common/gragh';
         },
         graph:null,
         initData:{},
+        customButtonDom:'',
         // ------- 添加分类弹窗
         classifyAddTitle:"添加分类",
         classifyAddShow:false,
@@ -316,7 +295,8 @@ import { myGraph } from './common/gragh';
         classifyForm:{
           SandboxClassifyId:0,
           SandboxClassifyName:'',
-          ChartPermissionId:null,
+          ChartPermissionId:0,
+          ChartPermissionName:'',
           ParentId:0,
           Level:0
         },
@@ -337,8 +317,7 @@ import { myGraph } from './common/gragh';
           chartName:"",
           classifyId:""
         },
-        saveOtherClassifys:'',
-        locationActiveNode:'',
+        saveOtherClassifys:[],
         operationType:'view',
         popoverVisible:false,
         popoverFlod:true,
@@ -347,48 +326,27 @@ import { myGraph } from './common/gragh';
         checkedLinkList:[],
         linkNode:null,
         popoverTimeout:null,
-        isSlideLeft:false
+        isSlideLeft:false,
+        locationParentIds:''
       }
     },
     created(){
       this.getClassify()
+      if(sessionStorage.getItem("sandboxEditBack")){
+        this.locationParentIds=sessionStorage.getItem("sandboxEditBack").split(',')
+        this.getSandboxDetailFun(this.locationParentIds[this.locationParentIds.length-1])
+      }else{
+        this.getSandboxList()
+      }
       this.getSandboxClassify()
-      this.getSandboxClassifyOnly()
-      this.getSandboxList()
-      // this.setting.async={
-        // enable:false,
-        // type:'head'
-      //   url:(()=>{
-      //     console.log(process.env);
-      //     // console.log(process.env.VUE_APP_API_ROOT,'process.env.VUE_APP_API_ROOT');
-      //     // if(process.env.NODE_ENV === "development"){
-      //     //   return "http://8.136.199.33:7777/adminapi/sandbox/classify/list"
-      //     // }else{
-      //       return process.env.VUE_APP_API_ROOT+'/sandbox/classify/list'
-      //     // }
-      //   })(),
-      //   type:'get',
-      //   autoParam: ["SandboxClassifyId"],
-      //   otherParam:{ "IsShowMe":this.isOnlyMe},
-      //   dataFilter: (treeId,parentNode,responseData)=>{
-      //     console.log(treeId,parentNode,responseData,'responseData');
-      //     return responseData.Data.AllNodes
-      //   },
-      //   headers:{
-      //     Authorization:localStorage.getItem("auth"),
-      //     Uuid:localStorage.getItem("uuid") || "",
-      //     AccessToken:localStorage.getItem("uuid")+"--zheshiyigename",
-      //   }
-      // }
     },
     mounted(){
-      // this.init()
       this.popoverDom = $('#link-popover')[0];
       this.popoverTriggerDom = $('#link-reference')[0];
       
       this.popoverDom.addEventListener('mouseenter',this.clearPopoverTimeout)
       this.popoverDom.addEventListener('mouseleave',this.closePopover)
-
+      this.customButtonDom = $('#custom-button-zone')[0]
     },
     beforeDestroy(){
       this.popoverDom.removeEventListener('mouseenter',this.clearPopoverTimeout)
@@ -401,20 +359,20 @@ import { myGraph } from './common/gragh';
         customInterence.getvariety({
           CompanyType: 'ficc'
         }).then(res => {
-          console.log(res);
           if(res.Ret !== 200)  return
             this.classifyArr = res.Data.List||[ ]
         })
       },
       // 获取沙盘图分类
       getSandboxClassify(parentNode,locationNode) {
-        console.log(parentNode,locationNode,'parentNode,locationNode');
+        // parentNode--父节点
+        // locationNode--展开父节点后需要定位的子节点,拖拽后
         const sandboxClassifyId = parentNode ? parentNode.SandboxClassifyId:0
         sandInterface.getSandboxClassify({SandboxClassifyId:sandboxClassifyId,IsShowMe:this.searchParams.IsShowMe}).then(res=>{
-          console.log(res);
           if (res.Ret === 200) {
             let nodesData=res.Data.AllNodes || []
             nodesData.map(item =>{
+              // 添加是否是目录的字段
               item.isCatalogue = item.SandboxId?false:true
             })
             if(parentNode){
@@ -422,46 +380,45 @@ import { myGraph } from './common/gragh';
             }else{
               this.treeData = nodesData
             }
-            if(locationNode){
+            let location = locationNode || this.locationNode
+            if(location){
               let searchNode
-              let locationId = locationNode.isCatalogue?locationNode.SandboxClassifyId:locationNode.SandboxId
+              let locationId = location.isCatalogue?location.SandboxClassifyId:location.SandboxId
               if(parentNode){
-                if(locationNode.isCatalogue){
+                if(location.isCatalogue){
                   searchNode=this.zTreeObj.getNodesByParam('SandboxClassifyId',+locationId,parentNode)
                 }else{
                   searchNode=this.zTreeObj.getNodesByParam('SandboxId',+locationId,parentNode)
                 }
-                console.log(searchNode,'searchNode');
                 if(!(searchNode && searchNode.length>0)) return 
                 this.zTreeClick(null,'',searchNode[0],1)
                 this.zTreeObj.selectNode(searchNode[0])
               }else{
                 requestAnimationFrame(()=>{
                   searchNode=this.zTreeObj.getNodesByParam('SandboxClassifyId',+locationId)
-                  console.log(searchNode,'searchNode');
                   if(!(searchNode && searchNode.length>0)) return 
                   this.zTreeClick(null,'',searchNode[0],1)
                   this.zTreeObj.selectNode(searchNode[0])
                 })
               }
+              this.locationNode=''
+            }else if(this.locationParentIds && this.locationParentIds.length>0){
+              // 获取分类列表后需要定位
+              requestAnimationFrame(()=>{
+                this.sandboxLocation(this.locationParentIds)
+                this.locationParentIds=[]
+                sessionStorage.removeItem('sandboxEditBack')
+              })
 
             }
           }
         })
       },
-      getSandboxClassifyOnly(){
-        sandInterface.getSandboxClassifyOnly().then(res=>{
-          if (res.Ret === 200) {
-            this.onlyClassifyTreeData=res.Data.AllNodes || []
-          }
-        })
-      },
       getSandboxList(type){
         if(type == 'setCurrentIndex'){
           this.searchParams.CurrentIndex=1
         }
         sandInterface.getSandboxListV2(this.searchParams).then(res=>{
-          console.log(res,'res');
           if(res.Ret == 200){
             if(!res.Data){
               this.pictureList=[]
@@ -477,45 +434,43 @@ import { myGraph } from './common/gragh';
           }
         })
       },
+      getSandboxDetailFun(SandboxId){
+        sandInterface.getSandboxDetail({SandboxId:SandboxId || this.activeNode.SandboxId}).then(res=>{
+          if(res.Ret == 200){
+            this.viewSandbox=res.Data
+            this.rightType='chart'
+            this.initData = JSON.parse(this.viewSandbox.Content)
+          }
+        })
+      },
       getZTree(zTree){
         this.zTreeObj=zTree
       },
-      getSelectZTree(zTree){
-        this.selectZTreeObj=zTree
-      },
-      selectClassify(event,treeId,treeNode,clickFlag){
-        this.sandSaveParams.SandboxClassifyId = treeNode.SandboxClassifyId
-        this.selectClassifyShow = false
-      },
       zTreeDragBefore(treeId,treeNodes){
         return this.permissionBtn.isShowBtn('sandboxPermission','sandbox_classify_move')
       },
       zTreeDropBefore(treeId,treeNodes,targetNode,moveType,isCopy){
-        console.log(treeId,treeNodes,targetNode,moveType,isCopy,'zTreeDropBefore');
         if((!targetNode) && (!moveType)) return false
         if(((!treeNodes[0].isCatalogue) && targetNode.Level==1 && moveType!='inner')||
         ((!targetNode.isCatalogue) && moveType=='inner')||
         (treeNodes[0].isCatalogue && targetNode.Level==5 && moveType=='inner')){
           return false
         }
-
-        // 接口失败后的还原
-        // this.movingRecoveData.treeNode=treeNodes[0]
-        // if(treeNodes[0].getPreNode()){
-        //   this.movingRecoveData.targetNode = treeNodes[0].getPreNode()
-        //   this.movingRecoveData.moveType = "next"
-        // }else if(treeNodes[0].getNextNode()){
-        //   this.movingRecoveData.targetNode = treeNodes[0].getNextNode()
-        //   this.movingRecoveData.moveType = "prev"
-        // }else if(treeNodes[0].getParentNode()){
-        //   this.movingRecoveData.targetNode = treeNodes[0].getParentNode()
-        //   this.movingRecoveData.moveType = "inner"
-        // }
+        // 用于接口失败后的还原位置
+        this.movingRecoveData.treeNode=treeNodes[0]
+        if(treeNodes[0].getPreNode()){
+          this.movingRecoveData.targetNode = treeNodes[0].getPreNode()
+          this.movingRecoveData.moveType = "next"
+        }else if(treeNodes[0].getNextNode()){
+          this.movingRecoveData.targetNode = treeNodes[0].getNextNode()
+          this.movingRecoveData.moveType = "prev"
+        }else if(treeNodes[0].getParentNode()){
+          this.movingRecoveData.targetNode = treeNodes[0].getParentNode()
+          this.movingRecoveData.moveType = "inner"
+        }
       },
       zTreeDrop(e,treeId,treeNodes,targetNode,moveType,isCopy){
-        console.log(treeId,treeNodes[0],targetNode,moveType,isCopy,'zTreeDrop');
         if((!targetNode) && (!moveType)) return
-        console.log(treeNodes[0].getParentNode(),targetNode.getParentNode());
         let parentNode = treeNodes[0].getParentNode()
         let prevNode = treeNodes[0].getPreNode()
         let nextNode = treeNodes[0].getNextNode()
@@ -536,48 +491,26 @@ import { myGraph } from './common/gragh';
               this.zTreeObj.removeChildNodes(parentNode)
             }
             this.getSandboxClassify(parentNode,treeNodes[0])
+          }else{
+            // 接口失败后的还原位置
+            let result=this.zTreeObj.moveNode(this.movingRecoveData.targetNode,this.movingRecoveData.treeNode,this.movingRecoveData.moveType)
+            if(!result){
+              this.$message.error('目录位置还原失败,刷新目录')
+              setTimeout(()=>{
+                this.getSandboxClassify()
+              },1000)
+            }
           }
         })
-        // if((!targetNode) && (!moveType)) return
-        // let parentNode = treeNodes[0].getParentNode()
-        // let prevNode = treeNodes[0].getPreNode()
-        // let nextNode = treeNodes[0].getNextNode()
-        // //移动分类
-        // let params={
-        //   ClassifyId:treeNodes[0].SandboxClassifyId,
-        //   SandboxId:treeNodes[0].isCatalogue?0:treeNodes[0].SandboxId,
-        //   ParentClassifyId:parentNode?parentNode.SandboxClassifyId:0,
-        //   PrevId:prevNode? (prevNode.isCatalogue?prevNode.SandboxClassifyId:prevNode.SandboxId) :0,
-        //   NextId:nextNode? (nextNode.isCatalogue?nextNode.SandboxClassifyId:nextNode.SandboxId) :0,
-        //   PrevType:prevNode?(prevNode.isCatalogue?1:2):0,
-        //   NextType:nextNode?(nextNode.isCatalogue?1:2):0
-        // }
-        // console.log(params);
-        // sandInterface.sandboxClassifyMove(params).then(res=>{
-        //   if(res.Ret == 200){
-        //     this.$message.success('移动分类成功')
-        //   }else{
-        //     // let result=this.zTreeObj.moveNode(this.movingRecoveData.targetNode,this.movingRecoveData.treeNode,this.movingRecoveData.moveType)
-        //     // if(!result){
-        //     //   window.location.reload()
-        //     // }
-        //   }
-        // })
       },
       zTreeExpandBefore(treeId, treeNode){
-        // console.log( treeId, treeNode);
         if(treeNode.Children && treeNode.Children.length>0){
           return true
         }else{
           this.getSandboxClassify(treeNode)
         }
       },
-      zTreeExpand(event, treeId, treeNode){
-        // console.log(event, treeId, treeNode);
-      },
       zTreeClick(event, treeId, treeNode,clickFlag){
-        console.log(event, treeId, treeNode,clickFlag);
-
         if(clickFlag==1){
           if(this.activeNode.SandboxClassifyId == treeNode.SandboxClassifyId &&
           this.activeNode.SandboxId == treeNode.SandboxId){
@@ -589,26 +522,18 @@ import { myGraph } from './common/gragh';
             this.searchParams.CurrentIndex=1
             this.searchParams.SandboxClassifyId = treeNode.SandboxClassifyId
             this.getSandboxList('setCurrentIndex')
-            // this.rightType='list'
           }else{
             // 沙盘图
-            sandInterface.getSandboxDetail({SandboxId:this.activeNode.SandboxId}).then(res=>{
-              console.log(res);
-              if(res.Ret == 200){
-                this.viewSandbox=res.Data
-                this.rightType='chart'
-                this.initData = JSON.parse(this.viewSandbox.Content)
-              }
-            })
+            this.getSandboxDetailFun()
           }
         }
 
         if(clickFlag==0 && treeNode.isCatalogue){
+          // 取消选中
           this.activeNode={}
           this.searchParams.CurrentIndex=1
           this.searchParams.SandboxClassifyId = ''
           this.getSandboxList('setCurrentIndex')
-          // this.rightType='list'
         }
       },
       onlyMeHandler(){
@@ -618,7 +543,6 @@ import { myGraph } from './common/gragh';
         this.getSandboxList('setCurrentIndex')
       },
       searchHandle(query) {
-        // console.log(query,"搜索");
         this.searchCurrentIndex = 1;
         this.KeyWord = query;
         this.searchSandbox()
@@ -641,7 +565,6 @@ import { myGraph } from './common/gragh';
       },
       searchLoad() {
         // 加载更多
-        console.log("加载更多");
         if(!this.searchHaveMore) return;
         this.searchCurrentIndex++
         this.searchSandbox()
@@ -649,7 +572,6 @@ import { myGraph } from './common/gragh';
       /* 聚焦获取当前检索 */
       inputFocusHandle(e) {
         // 选取
-        console.log('选取',e.target.value);
         this.searchCurrentIndex = 1;
         this.KeyWord = e.target.value;
         if(this.KeyWord) {
@@ -678,12 +600,13 @@ import { myGraph } from './common/gragh';
       },
       addClassify(e,node){
         e.stopPropagation()
-        console.log(node);
         this.addClassifyNodeTid = node.tId
         this.lastLevelClassifyName = this.getParentNodeName(node)
         this.classifyAddTitle="添加分类"
         this.classifyForm.Level = node.Level
         this.classifyForm.ParentId = node.SandboxClassifyId
+        this.classifyForm.ChartPermissionId = node.ChartPermissionId
+        this.classifyForm.ChartPermissionName = node.ChartPermissionName
         this.classifyAddShow=true
       },
       getParentNodeName(node){
@@ -695,13 +618,16 @@ import { myGraph } from './common/gragh';
         }
       },
       editClassify(e,node){
-        console.log(node);
         e.stopPropagation()
+        this.locationNode = node
         let pNode = node.getParentNode()
         this.lastLevelClassifyName = pNode ? pNode.SandboxClassifyName:''
         this.classifyForm.SandboxClassifyId = node.SandboxClassifyId
+        this.classifyForm.Level = node.Level
         this.classifyForm.SandboxClassifyName = node.SandboxClassifyName
-        this.classifyAddTitle="重命名"
+        this.classifyForm.ChartPermissionId = node.ChartPermissionId
+        this.classifyForm.ChartPermissionName = node.ChartPermissionName
+        this.classifyAddTitle="编辑分类"
         this.classifyAddShow=true
       },
       deleteClassify(e,node){
@@ -740,8 +666,7 @@ import { myGraph } from './common/gragh';
                 res.Data.DeleteStatus === 0 && node.SandboxId 
                 ? this.delHandle(node.SandboxClassifyId, node.SandboxId, 1)
                 : this.delHandle(node.SandboxClassifyId, node.SandboxId);
-              }).catch(() => {         
-              });
+              }).catch(() => {});
           }
         })
       },
@@ -754,16 +679,15 @@ import { myGraph } from './common/gragh';
           if (res.Ret === 200) {
             this.zTreeObj.removeNode(currentNode)
             this.$message.success(res.Msg);
-            // if(type && res.Data.SandboxId){
-              
-            // }else{
-
-            // }
           }
         });
       },
+      chartPermissionIdChange(value){
+        this.classifyForm.ChartPermissionName=
+        this.$refs.chartPermissionCascader.getCheckedNodes()[0] ?
+        this.$refs.chartPermissionCascader.getCheckedNodes()[0].label+'' || '':''
+      },
       classifyAddSubmit(){
-        console.log(this.classifyForm);
         //提交
         this.$refs.classifyFormRef.validate(valid=>{
           if(valid){
@@ -771,17 +695,23 @@ import { myGraph } from './common/gragh';
               //编辑
               let params={
                 SandboxClassifyId:this.classifyForm.SandboxClassifyId,
-                SandboxClassifyName:this.classifyForm.SandboxClassifyName
+                SandboxClassifyName:this.classifyForm.SandboxClassifyName,
+                ChartPermissionId:this.classifyForm.ChartPermissionId,
+                ChartPermissionName:this.classifyForm.ChartPermissionName,
               }
               sandInterface.editSandboxClassify(params).then(res=>{
                 if(res.Ret == 200){
                   this.classifyAddShow=false
                   this.$message.success(this.classifyAddTitle+"成功")
-                  let currentNode = this.zTreeObj.getSelectedNodes()[0]
+                  let parentNode = this.locationNode.getParentNode()
 
-                  currentNode.SandboxClassifyName = this.classifyForm.SandboxClassifyName
-                  this.zTreeObj.updateNode(currentNode)
-                  // this.getSandboxClassify()
+                  if(!parentNode){
+                    this.getSandboxClassify(null,this.locationNode)
+                  }else{
+                    this.zTreeObj.removeChildNodes(parentNode)
+                    this.zTreeObj.expandNode(parentNode,true,false,false,true)
+                    this.zTreeClick(null,'',this.locationNode,1)
+                  }
                 }
               })
             }else{
@@ -790,7 +720,8 @@ import { myGraph } from './common/gragh';
                 SandboxClassifyName:this.classifyForm.SandboxClassifyName,
                 ParentId:this.classifyForm.ParentId,
                 Level:this.classifyForm.Level,
-                ChartPermissionId:this.classifyForm.ChartPermissionId
+                ChartPermissionId:this.classifyForm.ChartPermissionId,
+                ChartPermissionName:this.classifyForm.ChartPermissionName,
               }
               sandInterface.addSandboxClassify(params).then(res=>{
                 if(res.Ret == 200){
@@ -800,11 +731,8 @@ import { myGraph } from './common/gragh';
                     this.getSandboxClassify()
                   }else{
                     let curNode = this.zTreeObj.getNodeByTId(this.addClassifyNodeTid)
-                    // console.log(curNode,'');
                     this.zTreeObj.removeChildNodes(curNode)
-                    // this.$nextTick(()=>{
-                      this.zTreeObj.expandNode(curNode,true,false,false,true)
-                    // })
+                    this.zTreeObj.expandNode(curNode,true,false,false,true)
                   }
                 }
               })
@@ -817,7 +745,8 @@ import { myGraph } from './common/gragh';
         this.classifyForm={
           SandboxClassifyId:0,
           SandboxClassifyName:'',
-          ChartPermissionId:null,
+          ChartPermissionId:0,
+          ChartPermissionName:'',
           ParentId:0,
           Level:0
         }
@@ -829,13 +758,9 @@ import { myGraph } from './common/gragh';
         let scrollTop = this.$refs.pictureListRef.scrollTop;
         let clientHeight = this.$refs.pictureListRef.clientHeight;
         let scrollHeight = this.$refs.pictureListRef.scrollHeight;
-        // console.log('scrollTop:',scrollTop)
-        // console.log('clientHeight:',clientHeight)
-        // console.log('scrollHeight:',scrollHeight)
         if(scrollTop + clientHeight >= scrollHeight-10 && this.pictureHaveMore){
           this.searchParams.CurrentIndex++
           this.getSandboxList();
-          console.log("加载更多");
         }
       },300),
       /* 展示详情 */
@@ -849,19 +774,10 @@ import { myGraph } from './common/gragh';
         this.sandboxLocation(ParentClassifys)
       },
       async sandboxLocation(ParentClassifys){
-        console.log(ParentClassifys,'ParentClassifys');
         let beActiveNode=''
         for (let i = 0; i < ParentClassifys.length; i++) {
           const element = ParentClassifys[i];
-          // if(i == (ParentClassifys.length-1)){
-          //   console.log(+element,beActiveNode,'element');
-          //   let searchNode=this.zTreeObj.getNodesByParam('SandboxId',+element,beActiveNode)
-          //   console.log(searchNode[0]);
-          //   this.zTreeObj.selectNode(searchNode[0],false,false)
-          //   return 
-          // }
           let searchNode=this.zTreeObj.getNodesByParam('SandboxClassifyId',+element,beActiveNode)
-          console.log(searchNode,'searchNode',i);
           if(!(searchNode&&searchNode.length>0)){
             if(!beActiveNode){
               break
@@ -883,12 +799,11 @@ import { myGraph } from './common/gragh';
             beActiveNode = searchNode[0]
           }
         }
-        this.locationActiveNode=beActiveNode
         // 选中
-        requestAnimationFrame(()=>{
+        this.activeNode = beActiveNode
+        setTimeout(()=>{
           this.zTreeObj.selectNode(beActiveNode)
-          this.activeNode = beActiveNode
-        })
+        },300)
       },
       expandNodeAsync(parentNode,activeId,isLast=false){
         return new Promise((resolve,reject)=>{
@@ -898,7 +813,6 @@ import { myGraph } from './common/gragh';
           }else{
             resultNode = this.zTreeObj.getNodesByParam('SandboxClassifyId',activeId,parentNode)
           }
-          console.log(resultNode,'resolve(resultNode)');
           if(resultNode.length>0){
             resolve(resultNode)
             return 
@@ -924,7 +838,6 @@ import { myGraph } from './common/gragh';
       },
       copyHandle:_.debounce(function ({ PicUrl }){
         if(!PicUrl) return this.$message('暂无内容可复制')
-        console.log("复制");
         this.lockLoding = this.$loading({
           lock: true,
           text: '复制图片中...',
@@ -965,7 +878,6 @@ import { myGraph } from './common/gragh';
         }
       },500),
       deleteHandle(item,type){
-        console.log(item,type);
         this.$confirm("确定删除该沙盘图吗?", "提示", {
           type: "warning",
         })
@@ -977,7 +889,6 @@ import { myGraph } from './common/gragh';
             if (res.Ret === 200) {
               this.$message.success(res.Msg);
               let result = this.zTreeObj.getNodesByParam('SandboxId',item.SandboxId)[0]
-              console.log(result);
               if(type=='inList'){
                 // 在沙盘图列表中删除
                 if(result){
@@ -985,8 +896,10 @@ import { myGraph } from './common/gragh';
                 }
               }else{
                 // 在沙盘图详情删除
-                this.sandboxLocation([result.SandboxClassifyId])
                 this.zTreeObj.removeNode(result)
+                let parentNode = result.getParentNode()
+                this.zTreeObj.selectNode(parentNode)
+                this.zTreeClick(null,'',parentNode,1)
               }
               this.getSandboxList('setCurrentIndex')
               
@@ -995,16 +908,15 @@ import { myGraph } from './common/gragh';
         })
         .catch(() => {});
       },
-      addDiyDom(treeId, treeNode) {
-        // console.log(treeNode,'treeId, treeNode');
+      addHoverDom(treeId, treeNode){
         var aObj = $("#" + treeNode.tId + "_a");
-        if ($("#diyBtn_"+treeNode.SandboxClassifyId).length>0) return;
+        if (aObj.find('#custom-button-zone').length>0) return;
+        let dom = this.customButtonDom.cloneNode(true)
+        let addClassifyDom = $(dom).find('.add-classify-img')[0]
+        let editClassifyDom = $(dom).find('.edit-classify-img')[0]
+        let delClassifyDom = $(dom).find('.delete-classify-img')[0]
         if(treeNode.isCatalogue){
           //目录添加自定义按钮
-          let dom = $('#custom-button-zone')[0].cloneNode(true)
-          let addClassifyDom = $(dom).find('.add-classify-img')[0]
-          let editClassifyDom = $(dom).find('.edit-classify-img')[0]
-          let delClassifyDom = $(dom).find('.delete-classify-img')[0]
           if(treeNode.Level>4){
             addClassifyDom && (addClassifyDom.style.display='none')
           }else{
@@ -1013,15 +925,13 @@ import { myGraph } from './common/gragh';
           editClassifyDom && editClassifyDom.addEventListener("click",(e)=>this.editClassify(e,treeNode))
           delClassifyDom && delClassifyDom.addEventListener("click",(e)=>this.deleteClassify(e,treeNode))
 
-          aObj.append(dom);
+        }else{
+          addClassifyDom && (addClassifyDom.style.display='none')
+          editClassifyDom && (editClassifyDom.style.display='none')
+          delClassifyDom && (delClassifyDom.style.display='none')
         }
-
+        aObj.append(dom);
       },
-      // removeHoverDom(treeId, treeNode) {
-      //   console.log(treeId, treeNode,'treeId, treeNode');
-      //   $("#diyBtn_"+treeNode.SandboxClassifyId).unbind().remove();
-	    //   $("#diyBtn_space_" +treeNode.SandboxClassifyId).unbind().remove();
-      // }
       addSand(){
         const { href } = this.$router.resolve({ path: '/sandflow' });
         window.open(href, '_blank');
@@ -1031,12 +941,21 @@ import { myGraph } from './common/gragh';
         window.open(href, '_blank');
       },
       saveOther(){
+        sandInterface.getSandboxClassifyOnly().then(res=>{
+          if (res.Ret === 200) {
+            this.onlyClassifyTreeData=res.Data.AllNodes || []
+          }
+        })
         this.saveOtherForm.chartName = this.viewSandbox.Name+"(1)"
         this.saveOtherShow=true
       },
+      classifyTreeChange(value){
+        if(!value){
+          this.saveOtherForm.classifyId=''
+        }
+        this.saveOtherForm.classifyId=value[value.length-1]
+      },
       saveOtherSubmit: _.debounce( function() {
-        // console.log(this.viewSandbox);
-        // return 
         if(!this.graph.toJSON().cells.length) return this.$message.warning('画布无内容');
         this.$refs.saveOtherFormRef.validate(valid=>{
           if(valid){
@@ -1068,12 +987,13 @@ import { myGraph } from './common/gragh';
                 this.lockLoding.close();
                 return;
               }
-              console.log(sandData,'sandData');
               this.lockLoding.close();
               this.$message.success("另存为成功")
-              let saveOtherIds = this.saveOtherClassifys.split(',')
-              saveOtherIds.push(sandData.SandboxId)
-              this.sandboxLocation(saveOtherIds)
+              //沙盘图详情
+              this.getSandboxDetailFun(sandData.SandboxId)
+
+              this.saveOtherClassifys.push(sandData.SandboxId)
+              this.sandboxLocation(this.saveOtherClassifys)
               this.saveOtherShow=false
             },{
               preserveDimensions:true,//让svg为实际图片大小
@@ -1088,19 +1008,6 @@ import { myGraph } from './common/gragh';
                 svg.setAttribute('viewBox',`${x-30} ${y-30} ${width+60} ${height+60}`)
                 // // 在图表右下方 加上"来源:弘则研究"字样
                 // let gNode = svg.getElementsByClassName('x6-graph-svg-viewport')[0]
-                // 去掉不该截图的添加图标
-                let leftImg = svg.getElementsByClassName('left-topic-image')
-                for (let i = 0; i < leftImg.length; i++) {
-                  const element = leftImg[i];
-                  element.parentElement.removeChild(element)
-                  i--
-                }
-                let rightImg = svg.getElementsByClassName('right-topic-image')
-                for (let i = 0; i < rightImg.length; i++) {
-                  const element = rightImg[i];
-                  element.parentElement.removeChild(element)
-                  i--
-                }
                 // let textNode = document.createElement('text')
                 // textNode.setAttribute('x',(x-tx+width)/zoom-85)
                 // textNode.setAttribute('y',(y-ty+height)/zoom+27)
@@ -1117,6 +1024,12 @@ import { myGraph } from './common/gragh';
                 .x6-port {
                     visibility: hidden;
                 }
+                .left-topic-image{
+                  visibility:hidden;
+                }
+                .right-topic-image{
+                  visibility: hidden;
+                }
                 ` 
             })
           }
@@ -1126,10 +1039,11 @@ import { myGraph } from './common/gragh';
       },500),
       saveOtherClosed(){
         this.saveOtherForm={
-          chartName:"逻辑图名称",
+          chartName:"",
           classifyId:""
         }
-        this.selectZTreeObj.cancelSelectedNode()
+        this.saveOtherClassifys=[]
+        // this.selectZTreeObj.cancelSelectedNode()
         this.$refs.saveOtherFormRef.clearValidate()
       },
       copySandHandle: _.debounce(function() {
@@ -1153,8 +1067,6 @@ import { myGraph } from './common/gragh';
         img.onload = ()=>{
           canvas.width = img.width;
           canvas.height = img.height;
-          // console.log('width',img.width)
-          // console.log('height',img.height)
           ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
           ctx.fillStyle="#fff";
           ctx.fillRect(0, 0, img.width, img.height);
@@ -1182,26 +1094,13 @@ import { myGraph } from './common/gragh';
         preserveDimensions:true,//让svg为实际图片大小
         beforeSerialize:(svg)=>{
           const {x,y,width,height} = this.graph.getContentBBox(cells)
-          let {tx,ty} = this.graph.translate()
+          // let {tx,ty} = this.graph.translate()
           //给导出的svg增加一点宽高
           svg.setAttribute('width',width+60)
           svg.setAttribute('height',height+60) 
           //设置viewBox使图像居中
           svg.setAttribute('viewBox',`${x-30} ${y-30} ${width+60} ${height+60}`)
           // let gNode = svg.getElementsByClassName('x6-graph-svg-viewport')[0]
-          // 去掉不该截图的添加图标
-          let leftImg = svg.getElementsByClassName('left-topic-image')
-          for (let i = 0; i < leftImg.length; i++) {
-            const element = leftImg[i];
-            element.parentElement.removeChild(element)
-            i--
-          }
-          let rightImg = svg.getElementsByClassName('right-topic-image')
-          for (let i = 0; i < rightImg.length; i++) {
-            const element = rightImg[i];
-            element.parentElement.removeChild(element)
-            i--
-          }
           // let textNode = document.createElement('text')
           // textNode.setAttribute('x',x-tx+width-90)
           // textNode.setAttribute('y',y-ty+height+22)
@@ -1210,38 +1109,28 @@ import { myGraph } from './common/gragh';
           // textNode.innerText = '来源:弘则研究'
           // gNode.appendChild(textNode)
             },
-        copyStyles:false,
-        stylesheet: `
+          copyStyles:false,
+          stylesheet: `
             svg{
                 background-color:white;
             }
           .x6-port {
               visibility: hidden;
+          }
+          .left-topic-image{
+            visibility:hidden;
+          }
+          .right-topic-image{
+            visibility: hidden;
           }` 
         })
       },500),
-      selectClassify(event,treeId,treeNode,clickFlag){
-        // console.log(treeNode);
-        this.saveOtherForm.classifyId = treeNode.SandboxClassifyId
-        this.saveOtherClassifys=this.getParentClassifyIds(treeNode)
-        this.selectClassifyShow = false
-      },
-      getParentClassifyIds(treeNode){
-        let parentNode = treeNode.getParentNode()
-        if(parentNode){
-          return this.getParentClassifyIds(parentNode)+','+treeNode.SandboxClassifyId
-        }else{
-          return treeNode.SandboxClassifyId+''
-        }
-      },
       //==============================画布
       // 初始化画布
       init() {
-        const graph = new myGraph('sand-chart-body',null,'','view');
-        console.log(graph,'graph');
+        const graph = new myGraph('sand-chart-body',null,'view');
         this.graph = graph;
         graph.on('node:mouseenter', ({ node, e }) => {
-          console.log(node);
           let data = node.data
           this.linkNode = node
           if(data && data.linkData && data.linkData.length>0){
@@ -1266,10 +1155,6 @@ import { myGraph } from './common/gragh';
                 })
                 let clinetPositon=graph.localToClient(node.position())
                 let size=node.size()
-                console.log(clinetPositon,'clinetPositon',node.position());
-                // const dom = $('#link-reference')[0];
-                // console.log(this.popoverTriggerDom,'domdomdom');
-                console.log(clinetPositon.x+size.width/2,clinetPositon.y);
                 this.popoverTriggerDom.style.left = clinetPositon.x+size.width/2 + 'px';
                 this.popoverTriggerDom.style.top = clinetPositon.y + 'px';
                 this.popoverVisible=true
@@ -1300,7 +1185,6 @@ import { myGraph } from './common/gragh';
         })
       },
       navigateTo(item){
-        console.log(item,'item');
         if(item.Type == 1){
           if(item.databaseType==0){
             // 普通指标

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff