浏览代码

区间分析

jwyu 8 月之前
父节点
当前提交
1573925499

+ 66 - 0
src/api/modules/intervalAnalysis.js

@@ -0,0 +1,66 @@
+import http from "@/api/http.js"
+
+//区间分析
+export default{
+  // 分类列表
+  classifyList:params=>{
+    return http.get('/range_analysis/chart_classify/list',params)
+  },
+  /**
+	 * 图表和目录拖动排序
+	 */
+  classifyMoveSort:params=>{
+		return http.post('/range_analysis/chart_classify/move',params)
+	},
+  // 目录编辑
+  classifyEdit:params=>{
+		return http.post('/range_analysis/chart_classify/edit',params)
+	},
+  // 目录新增
+  classifyAdd:params=>{
+		return http.post('/range_analysis/chart_classify/add',params)
+	},
+  //分类删除检测
+  classifyDelCheck:params=>{
+    return http.post('/range_analysis/chart_classify/delete/check',params)
+  },
+  // 目录/图表删除
+  classifyDelete:params=>{
+		return http.post('/range_analysis/chart_classify/delete',params)
+	},
+  // 不含图表的所有目录
+  classifyListNoChart:params=>{
+    return http.get('/range_analysis/chart_classify/tree',params)
+  },
+
+  // 区间分析图列表
+  getChartList:params=>{
+    return http.get('/range_analysis/chart_info/list',params)
+  },
+  // 图表刷新
+  chartRefresh:params=>{
+    return http.get('/range_analysis/chart_info/refresh',params)
+  },
+  // 图表另存为
+  chartSaveOther:params=>{
+    return http.post('/range_analysis/chart_info/copy',params)
+  },
+  // 图表搜索
+  chartSearch:params=>{
+    return http.get('/range_analysis/chart_info/search_by_es',params)
+  },
+
+  // 图表预览
+  chartPreview:params=>{
+    return http.post('/range_analysis/chart_info/preview',params)
+  },
+  // 图表新增
+  chartAdd:params=>{
+    return http.post('/range_analysis/chart_info/add',params)
+  },
+  // 图表详情
+  chartInfo:params=>{
+    return http.get('/range_analysis/chart_info/detail',params)
+  }
+  
+}

+ 199 - 0
src/components/chart/chartListTableWrap.vue

@@ -0,0 +1,199 @@
+<template>
+  <div class="chart-list-cont">
+    <span>{{ $t("Chart.total_chart_show", { limit: total }) }}</span>
+    <div class="table-wrap" v-infinite-scroll="load" v-if="total">
+      <table class="table-box" cellpadding="0" cellspacing="0">
+        <thead class="table-head thead-sticky">
+          <tr>
+            <td
+              v-for="opt in tableColOpts"
+              :key="opt.colKey"
+              :style="{ width: opt.width }"
+            >
+              {{ opt.title }}
+            </td>
+          </tr>
+        </thead>
+        <tbody>
+          <tr
+            v-for="row in list"
+            :key="row.ChartInfoId"
+          >
+            <td
+              v-for="opt in tableColOpts"
+              :key="opt.colKey"
+              :style="{ width: opt.width }"
+            >
+              <span v-if="opt.colKey==='opt'">
+                <el-button type="text" @click="handleEdit(row)">{{$t('Table.edit_btn')}}</el-button>
+                <el-button type="text" @click="handleRefresh(row)">{{$t('Table.refresh_btn')}}</el-button>
+                <el-button type="text" @click="handleSaveOther(row)">{{$t('Table.save_as')}}</el-button>
+                <el-button type="text" style="color:#f00" @click="handleDelChart(row)">删除</el-button>
+              </span>
+              <span v-else-if="['CreateTime','ModifyTime'].includes(opt.colKey)">
+               {{formatTime(row[opt.colKey])}}
+              </span>
+              <div v-else-if="opt.colKey==='ChartName'" @click="handleClickName(row)">{{row[opt.colKey]}}</div>
+              <span v-else>{{ row[opt.colKey] }}</span>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <div v-if="!total" class="nodata">
+      <tableNoData :text="$t('Common.no_chart_msg')" />
+    </div>
+  </div>
+</template>
+<script>
+import moment from 'moment'
+export default {
+  props: ['total', 'list'],
+  computed: {
+    currentLang() {
+      return this.$store.state.lang
+    },
+  },
+  data() {
+    return {
+      tableColOpts: [
+        {
+          colKey: 'ChartName',
+          title: '名称',
+        },
+        {
+          colKey: 'SysUserRealName',
+          title: '创建人',
+          width:'120px'
+        },
+        {
+          colKey: 'CreateTime',
+          title: '创建时间',
+          width:'160px'
+        },
+        {
+          colKey: 'ModifyTime',
+          title: '更新时间',
+          width:'160px'
+        },
+        {
+          colKey: 'opt',
+          title: '操作',
+          width:'220px'
+        },
+      ]
+    }
+  },
+  mounted() {
+
+  },
+  methods: {
+    load(){
+      console.log('触底');
+      this.$emit('loadMoreHandle')
+    },
+
+    // 点击名称
+    handleClickName(e){
+      this.$emit('detailShowHandle',e)
+    },
+
+    handleEdit(e){
+      this.$emit('edit',e)
+    },
+
+    handleRefresh(e){
+      this.$emit('refresh',e)
+    },
+
+    handleSaveOther(e){
+      this.$emit('saveOther',e)
+    },
+
+    handleDelChart(e){
+      this.$emit('deleteChart',e)
+    },
+
+    formatTime(t) {
+      return moment(t).format('YYYY-MM-DD HH:mm:ss')
+    },
+
+    isShowBtn(item) {
+      const { path } = this.$route
+      const { statisticPermission, productPricePermission, checkPermissionBtn } = this.permissionBtn
+      //相关性分析
+      if (path === '/chartrelevance') {
+        return checkPermissionBtn(statisticPermission.corrAnalysis_addMyChart)
+      }
+      //拟合方程曲线
+      else if (path === '/fittingEquationList') {
+        return checkPermissionBtn(statisticPermission.fittingEq_addMyChart)
+      }
+      //统计特性
+      else if (path === '/statisticFeatureList') {
+        return checkPermissionBtn(statisticPermission.statisticFeature_addMyChart)
+      }
+      //跨品种分析
+      else if (path === '/crossVarietyChartList') {
+        return checkPermissionBtn(statisticPermission.crossVariety_addMyChart)
+      }
+      //商品价格曲线
+      else if (path === '/commordityChartBase') {
+        const { Source, ChartType } = item
+        if (Source === 2 && ChartType === 8) {//是商品价格曲线
+          return checkPermissionBtn(productPricePermission.goodsPrice_priceLine_addMyChart)
+        } else {//是利润曲线
+          return checkPermissionBtn(productPricePermission.goodsPrice_incomeLine_addMyChart)
+        }
+      }
+      return false
+    }
+  },
+}
+</script>
+<style scoped lang='scss'>
+.chart-list-cont {
+  color: #333;
+  
+  .table-wrap {
+    margin-top: 10px;
+    height: calc(100vh - 160px);
+    background-color: #fff;
+    overflow-y: auto;
+    .table-box {
+      width: 100%;
+      border-color: #dcdfe6;
+      td,
+      th {
+        word-break: break-all;
+        word-wrap: break-word;
+        border: 1px solid #dcdfe6;
+        height: 40px;
+        text-align: center;
+        border-left: none;
+        border-top: none;
+        &:first-child {
+          border-left: 1px solid #dcdfe6;
+        }
+      }
+      .thead-sticky {
+        position: sticky;
+        top: 0;
+      }
+      .table-head {
+        background-color: #ebeef5;
+        font-weight: bold;
+      }
+      tbody {
+        color: #666;
+        .active_row {
+          background-color: #ecf5ff;
+        }
+      }
+    }
+  }
+  .nodata {
+    text-align: center;
+  }
+}
+</style>

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

@@ -430,6 +430,25 @@ export default [
 					pathName_en:"Cross-Variety Analy"
 				}
 			},
+			{
+				path: 'rangeAnalysis',
+				name: '区间分析',
+				component: () => import('@/views/intervalAnalysis/list.vue'),
+				meta:{
+					name_en:"Interval Analysis"
+				},
+			},
+			{
+				path: 'rangeAnalysisChartEditor',
+				name: '添加图表',
+				component: () => import('@/views/intervalAnalysis/addChart.vue'),
+				meta:{
+					name_en:"edit Interval Analysis",
+					pathFrom: "rangeAnalysis",
+					pathName: "区间分析",
+					pathName_en:"Interval Analysis" 
+				},
+			},
 		]
 	},
 

+ 4 - 0
src/utils/registryComponents.js

@@ -42,3 +42,7 @@ Vue.component('dataLoading',dataLoading)
 //无指标/图表/表格权限缺省
 import noDataAuth from '@/components/noDataAuth.vue';
 Vue.component('noDataAuth',noDataAuth)
+
+// 图列表表格类型
+import chartListTableWrap from '@/components/chart/chartListTableWrap.vue'
+Vue.component('chartListTableWrap',chartListTableWrap)

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

@@ -51,6 +51,7 @@
 </template>
 
 <script>
+import apiIntervalAnalysis from '@/api/modules/intervalAnalysis'
 import { dataBaseInterface } from '@/api/api.js';
 import futuresInterface from '@/api/modules/futuresBaseApi';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi.js';
@@ -78,7 +79,7 @@ export default {
   },
   computed:{
     isRelevanceChart(){
-        return ['/chartrelevance'].includes(this.$route.path)||this.source===3||this.source===4
+      return ['/chartrelevance'].includes(this.$route.path)||[3,4,'interval_analysis'].includes(this.source)
     }
   },
   data() {
@@ -122,6 +123,8 @@ export default {
         res=await statisticFeatureInterface.classifyList()
       }else if([10,'cross_variety'].includes(this.source)) { //跨品种分析
         res = await crossVarietyInterface.classifyList();
+      }else if(['interval_analysis'].includes(this.source)){//区间分析
+        res=await apiIntervalAnalysis.classifyListNoChart()
       }else{
         res=await dataBaseInterface.chartClassify();
       }
@@ -138,6 +141,8 @@ export default {
         this.filterNodes(res.Data.AllNodes,1)
       }else if([10,'cross_variety'].includes(this.source)) { //
         this.filterNodes(res.Data.AllNodes,1)
+      }else if(['interval_analysis'].includes(this.source)){//区间分析
+        this.filterNodesAll(res.Data.AllNodes)
       }else{
         this.filterNodes(res.Data.AllNodes,3)
       }
@@ -182,6 +187,8 @@ export default {
         res=await statisticFeatureInterface.saveOtherChart(params)
       }else if([10,'cross_variety'].includes(this.source)) { //跨品种分析
         res = await crossVarietyInterface.saveOtherChart(params);
+      }else if(['interval_analysis'].includes(this.source)){//区间分析
+        res=await apiIntervalAnalysis.chartSaveOther(params)
       }else{
         res=await dataBaseInterface.saveChartOther(params)
       }

+ 22 - 2
src/views/dataEntry_manage/databaseComponents/openDialog.vue

@@ -85,6 +85,7 @@
 import { dataBaseInterface } from '@/api/api.js';
 import { unitArr } from '@/utils/defaultOptions';
 import { frequencyArr } from './util'
+import apiIntervalAnalysis from '@/api/modules/intervalAnalysis'
 export default {
 	props: {
 		isOpenDialog: {
@@ -162,13 +163,30 @@ export default {
 			let res = null;
 
 			if(this.title==='添加') {
-				res = await dataBaseInterface.nodeAdd({
+        // 区间分析
+        if(this.$route.path==='/rangeAnalysis'){
+          res=await apiIntervalAnalysis.classifyAdd({
+            ChartClassifyName:this.formData.levelVal||'',
+            ParentId:this.formData.parent_id || 0,
+						Level: this.formData.level
+          })
+        }else{
+          res = await dataBaseInterface.nodeAdd({
 						ClassifyName: this.formData.levelVal||'',
 						ParentId:this.formData.parent_id || 0,
 						Level: this.formData.level
 					})
+        }
+				
 			}else if(this.title==='编辑') {
-				res = this.formData.isEDB
+        // 区间分析
+        if(this.$route.path==='/rangeAnalysis'){
+          res=await apiIntervalAnalysis.classifyEdit({
+            ChartClassifyId: this.formData.classify_id || 0,
+            ChartClassifyName: this.formData.levelVal||''
+          })
+        }else{
+          res = this.formData.isEDB
 					? await dataBaseInterface.targetEdit({
 							ClassifyId: this.formData.level_menu[this.formData.level_menu.length - 1],
 							EdbInfoId: this.formData.edbinfo_id,
@@ -180,6 +198,8 @@ export default {
 							ClassifyName: this.formData.levelVal||'',
 							ClassifyId:this.formData.classify_id || 0
 						})
+        }
+				
 			}
 			if(res.Ret !== 200) return
 			this.$message.success(res.Msg);

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

@@ -157,6 +157,9 @@ export const chartSetMixin = {
       /* 雷达图 */
       radarChartData: {},
 
+      // 区间分析图数据
+      intervalAnalysisChartData:{},
+
       /* 修改对应版本信息弹窗  替换原有设置英文*/
 			isLangInfoDia: false
 		}
@@ -753,7 +756,7 @@ export const chartSetMixin = {
         const useTableLimit = ['/predictEdb','/addpredictEdb','/editpredictEdb','/chartThemeSet'].includes(this.$route.path)
         //非ETA图库图表也不设置自定义上下限,相关性和统计特征也会用到曲线图
         //若chartInfo.Source为1,需在之前调用setLimitData
-        const isETASource = [1,11].includes(this.chartInfo.Source)
+        const isETASource = [1,11,12].includes(this.chartInfo.Source)
         let minLimit = 0,maxLimit = 0
         if(useTableLimit||!isETASource){
             minLimit = newval[sameSideIndex].MinData
@@ -2586,6 +2589,21 @@ export const chartSetMixin = {
 
     },
 
+    // 区间分析
+    initIntervalAnalysisChartData(data){
+      // 
+      console.log('初始化区间分析数据');
+      this.tableData=data.EdbInfoList
+      this.chartInfo=data.ChartInfo
+      this.intervalAnalysisChartData=data.DataResp
+      this.setLimitData(this.tableData)
+      this.setIntervalAnalysisChart()
+    },
+    setIntervalAnalysisChart(){
+      console.log('渲染区间分析图表');
+      this.setDefaultChart(this.tableData)
+    },
+
 		/* 查询范围为1年内 x轴显示为月/日 否则默认年/月 */
 		xLabelDealHandle() {
 			const end =
@@ -2893,13 +2911,15 @@ export const chartSetMixin = {
       //其他source
       const sourceMap = {
         2: this.setCommodityChart,
-        10: this.setCrossVarietyChart
+        10: this.setCrossVarietyChart,
+        12:this.setIntervalAnalysisChart
         // 3:
       }
       if(this.chartInfo.Source === 1) {
         if([7,10,11].includes(this.chartInfo.ChartType)){
             typeMap[this.chartInfo.ChartType]()
         }else{
+          console.log('1111');
             this.setAddChartDefault&&this.setAddChartDefault();
             typeMap[this.chartInfo.ChartType](this.tableData)
         }

+ 692 - 0
src/views/intervalAnalysis/addChart.vue

@@ -0,0 +1,692 @@
+<template>
+  <div class="add-chart-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-wrap" v-show="!isSlideLeft" id="left">
+      <div class="left-top">
+        <div>
+          <el-button type="primary" @click="handleComputed">{{
+            $t("Dialog.calculate_btn")
+          }}</el-button>
+          <el-button type="primary" plain @click="$router.back()">{{
+            $t("Dialog.cancel_btn")
+          }}</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>
+          {{ $t("StatisticAnalysis.ChartRelevance.opt_tip_btn") }}
+        </div>
+      </div>
+      <div class="left-content">
+        <div class="type-box">
+          <span :class="['item', type == 0 ? 'active' : '']" @click="type = 0"
+            >单指标</span
+          >
+          <span :class="['item', type == 1 ? 'active' : '']" @click="type = 1"
+            >选择多指标</span
+          >
+        </div>
+        <!-- 单指标模块 -->
+        <singleEdbWrap ref="singleEdbWrap"/>
+
+      </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-wrap" id="right">
+      <div class="chart-min-cont" v-if="tableData.length">
+        <div class="cont-top">
+					<div class="top-left">
+						<template>
+							<el-button
+								type="primary"
+								v-for="item in yearSelector"
+								:key="item.value"
+								size="medium"
+								:plain="item.value !== year_select"
+								class="year-btn"
+								@click.native="changeYear(item)"
+								>{{ item.name }}</el-button
+							>
+							<el-button type="text" class="btn-sty" @click="openDateDia">{{
+								dateTip
+							}}</el-button>
+						</template>
+					</div>
+					<div class="top-left">
+						<el-button type="primary" size="medium" class="year-btn">保存指标</el-button>
+						<el-button type="primary" size="medium" class="year-btn" @click="showSaveChart=true">保存图</el-button>
+						<el-button type="primary" plain size="medium" class="year-btn">图表设置</el-button>
+					</div>
+				</div>
+        <div class="cont-bottom">
+					<div class="chart-show-cont" v-show="options.series">
+						<div class="chartWrapper" id="chartWrapper">
+							<h2 
+								class="chart-title" 
+                v-if="false"
+								v-show="chartInfo.ChartName"
+								:style="`
+									textAlign:${JSON.parse(chartInfo.ChartThemeStyle).titleOptions.align};
+									fontSize:${JSON.parse(chartInfo.ChartThemeStyle).titleOptions.style.fontSize}px;
+									color:${JSON.parse(chartInfo.ChartThemeStyle).titleOptions.style.color}
+								`"
+							>
+								{{ chartInfo.ChartName }}
+							</h2>
+
+							<Chart :options="options" :chartInfo="chartInfo" ref="chartRef"/>
+							<template>
+								<div class="range-cont left" v-if="leftIndex !== -1">
+									<el-input
+										style="width: 60px; display: block"
+										size="mini"
+										type="number"
+										placeholder="上限"
+										v-model="chartLimit.max"
+										@change="changeLimit"
+									/>
+									<el-input
+										class="min-data-input"
+										size="mini"
+										type="number"
+										placeholder="下限"
+										v-model="chartLimit.min"
+										@change="changeLimit"
+									/>
+								</div>
+								<div class="range-cont right" v-if="rightIndex !== -1">
+									<el-input
+										style="width: 60px; display: block"
+										size="mini"
+										type="number"
+										placeholder="上限"
+										v-model="chartLimit.rightMax"
+										@change="changeLimit"
+									/>
+									<el-input
+										class="min-data-input"
+										size="mini"
+										type="number"
+										placeholder="下限"
+										v-model="chartLimit.rightMin"
+										@change="changeLimit"
+									/>
+								</div>
+								<!-- 右2上下限设置 -->
+								<div class="range-cont rightTwo" v-if="rightTwoIndex !== -1">
+									<el-input
+										style="width: 60px; display: block"
+										size="mini"
+										type="number"
+										placeholder="上限"
+										v-model="chartLimit.rightTwoMax"
+										@change="changeLimit"
+									/>
+									<el-input
+										class="min-data-input"
+										size="mini"
+										type="number"
+										placeholder="下限"
+										v-model="chartLimit.rightTwoMin"
+										@change="changeLimit"
+									/>
+								</div>
+							</template>
+
+						</div>
+
+						<div class="chart-bottom-insruction-info" v-if="false">
+
+							<div class="chart-source">
+								<span
+									v-if="chartInfo.SourcesFrom"
+									:style="`
+									color: ${JSON.parse(chartInfo.SourcesFrom).isShow ? JSON.parse(chartInfo.SourcesFrom).color : '#999'};
+									fontSize: ${ JSON.parse(chartInfo.SourcesFrom).fontSize }px;
+								`"
+								><!-- 来源 -->{{$t('Chart.Detail.source')}}:{{ JSON.parse(chartInfo.SourcesFrom).text}}
+								</span>
+
+								<el-switch
+									v-if="chartInfo.SourcesFrom"
+									v-model="chartInfo.SourcesFromVisable"
+									:active-value="true"
+									:inactive-value="false"
+									style="margin:0 15px;"
+									@change="changeSourceVisable"
+								/>
+								<span class="editsty" @click="isShowSourceDialog=true"><!-- 编辑 -->{{$t('Chart.chart_edit_btn')}}</span>
+							</div>
+
+							<!-- 图表说明 -->
+							<div 
+								class="chart-instruction" 
+								v-if="chartInfo.Instructions&&JSON.parse(chartInfo.Instructions).isShow"
+								v-text="JSON.parse(chartInfo.Instructions).text"
+								:style="`
+									color: ${JSON.parse(chartInfo.Instructions).color};
+									fontSize: ${ JSON.parse(chartInfo.Instructions).fontSize }px
+								`"
+							></div>
+						</div>
+
+
+						<span class="chart-author"
+							><!-- 作者 -->{{$t('Chart.Detail.author')}}:{{ chartInfo.SysUserRealName || roleName }}</span
+						>
+					</div>
+					
+					<!-- 关联指标 -->
+					<edbTableSection :tableData="tableData"/>
+				</div>
+
+      </div>
+      <div class="nodata" v-else>
+				<tableNoData :text="$t('Common.no_info_msg')"/>
+			</div>
+
+
+    </div>
+
+		<!-- 日期端选择弹窗 -->
+    <DateChooseDia
+      :isDateDia="isDateDia"
+      :dateForm="dateForm"
+			:earliestDate="earliestDate"
+      @cancel="isDateDia = false"
+      @dateBack="dataChangeBack"
+    />
+
+		<!-- 保存图表 -->
+		<saveChart 
+			:isShow="showSaveChart"
+			:edbType="type"
+
+			@saveChart="handleSaveChart"
+			@close="showSaveChart=false"
+		/>
+  </div>
+</template>
+
+<script>
+import Chart from '@/views/dataEntry_manage/components/chart';
+import singleEdbWrap from './components/singleEdbWrap.vue';
+import apiIntervalAnalysis from '@/api/modules/intervalAnalysis'
+import { chartSetMixin } from '@/views/dataEntry_manage/mixins/chartPublic';
+import edbTableSection from '@/views/chartRelevance_manage/crossVarietyAnalysis/components/edbTableSection.vue';
+import DateChooseDia from '@/views/dataEntry_manage/components/DateChooseDia';
+import saveChart from './components/saveChart.vue'
+export default {
+  components:{
+		singleEdbWrap,
+		Chart,
+		edbTableSection,
+		DateChooseDia,
+		saveChart
+	},
+  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:{
+		roleName() {
+			return localStorage.getItem('userName');
+		},
+	},
+  data() {
+    return {
+      isSlideLeft: false,
+      type: 0,//类型 0单指标 1多指标
+
+
+			showSaveChart:false,//保存图
+
+    }
+  },
+  methods: {
+		// 保存图
+		handleSaveChart(e){
+			console.log(e);
+			const edbParams=this.$refs.singleEdbWrap.getParams()
+			const params={
+				ChartEdbInfoList:edbParams.ChartEdbInfoList,
+				ExtraConfig:JSON.stringify(edbParams.ExtraConfig),
+				ChartClassifyId:e.classify,
+				ChartName:e.name,
+				ChartType:1,
+				DateType:this.year_select,
+			}
+			apiIntervalAnalysis.chartAdd(params).then(res=>{
+				if(res.Ret===200){
+					this.showSaveChart=false
+					// {
+					// 		"Ret": 200,
+					// 		"Msg": "保存成功",
+					// 		"ErrMsg": "",
+					// 		"ErrCode": "",
+					// 		"Data": {
+					// 				"ChartInfoId": 2581,
+					// 				"UniqueCode": "96984be7b1681a6f9f5aa762191c3ed6",
+					// 				"ChartType": 9,
+					// 				"ClassifyId": 0
+					// 		},
+					// 		"Success": true
+					// }
+				}
+			})
+		},
+
+    //计算绘图
+    async handleComputed(){
+			
+      const edbParams=this.$refs.singleEdbWrap.getParams()
+			const params={
+				...edbParams,
+				DateType: this.year_select,
+				StartDate: [5 , 6].includes(this.year_select)
+					? dateArray[0]
+					: '',
+				EndDate: this.year_select === 5 ? dateArray[1]: '',
+				StartYear:this.count_year || 0,
+			}
+      console.log(params);
+      if(!params.ChartEdbInfoList.length||!params.ChartEdbInfoList[0].EdbInfoId){
+        this.$message.warning('请选择指标')
+        return
+      }
+
+      const res=await apiIntervalAnalysis.chartPreview(params)
+      if(res.Ret===200){
+        this.initIntervalAnalysisChartData(res.Data)
+      }
+    },
+
+		/* 年份改变 重新刷新图表接口  保存当前的图表配置和上下限 只改变图表 */
+		changeYear(item) {
+			this.year_select = item.value;
+			this.select_date = '';
+			this.dateTip =/* '请选择时间段' */ this.$t('Chart.choose_time');
+
+			this.handleComputed();
+		},
+
+		/* 打开时间段弹窗 */
+		openDateDia() {
+			// 自定义时间段回显
+			let selectDateStart = this.chartInfo.ChartType === 2?this.season_year[0]:this.select_date[0]
+			let selectDateEnd = this.chartInfo.ChartType === 2?this.season_year[1]:this.select_date[1]
+
+			this.dateForm = {
+				date_type: this.year_select,
+				start_date:
+					this.year_select === 5 || this.year_select === 6
+						? selectDateStart
+						: '',
+				end_date: this.year_select === 5 ? selectDateEnd : '',
+				count_year: this.year_select === 20 ? this.count_year : ''
+			};
+			this.isDateDia = true;
+		},
+		/* 保存完自定义日期 刷新数据  保存当前的图表配置和上下限 只改变图表*/
+		dataChangeBack(data) {
+			this.year_select = data.dateType;
+			this.isDateDia = false;
+      this.count_year = data.count_year
+
+			let dateStart = data.start_date
+			let dateEnd = data.end_date
+			let latestYear = parseInt(this.latestDate.substring(0,4))
+			if(data.dateType==20){
+				dateStart = `${latestYear-data.count_year+1}-01-01`
+				dateEnd = `${latestYear}-12-31`
+			}
+
+
+			// 因为原本季节性图不支持 DateType为5和6的 所以季节性也将StartDate和EndDate传过去
+			this.select_date = [dateStart, dateEnd];
+
+			if(data.dateType==20){
+				this.dateTip = /* `最近${data.count_year}年` */ this.$t('Chart.date_tip_count',{year:data.count_year})
+			}else if (data.dateType === 5) {
+				this.dateTip = `${data.start_date}~${data.end_date}`;
+			} else {
+				this.dateTip = /* `${data.start_date}~至今` */ this.$t('Chart.data_tip_since',{date:data.start_date});
+			}
+
+			this.handleComputed();
+		},
+
+
+    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() {
+    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>
+.add-chart-page {
+  .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;
+    }
+  }
+  display: flex;
+  .left-wrap {
+    width: 410px;
+    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-content {
+      padding: 20px;
+      max-height: calc(100vh - 190px);
+      overflow-y: auto;
+      .type-box {
+        .item {
+          font-size: 16px;
+          margin-right: 30px;
+          height: 24px;
+          line-height: 24px;
+          padding-bottom: 2px;
+          cursor: pointer;
+          display: inline-block;
+        }
+        .active {
+          color: #409eff;
+          border-bottom: 2px solid #409eff;
+        }
+      }
+    }
+  }
+  .right-wrap {
+    $font-small: 12px; 
+    $font-normal: 14px;
+    flex: 1;
+    background: #fff;
+    border: 1px solid #ececec;
+    height: calc(100vh - 118px);
+    overflow: auto;
+    /* overflow: hidden; */
+    border-radius: 4px;
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.05);
+    .mx-datepicker {
+      width: 220px !important;
+    }
+		 /* =================== */
+		.chart-min-cont {
+			background: #fff;
+			border: 1px solid #ececec;
+			height: calc(100vh - 118px);
+			overflow: auto;
+			/* overflow: hidden; */
+			border-radius: 4px;
+			box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.05);
+			.cont-top {
+				padding: 12px 30px;
+				border-bottom: 1px solid #ececec;
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+				box-shadow: 0px 3px 6px rgba(167, 167, 167, 0.09);
+				.top-left {
+					.year-btn {
+						margin-right: 5px;
+						margin-bottom: 5px;
+					}
+					.btn-sty {
+						border: 1px solid #409eff;
+					}
+					.date-setting{
+						border: 1px solid #DCDFE6;
+						border-radius: 4px;
+						cursor: pointer;
+						color: #333333;
+						line-height: 16px;
+					}
+				}
+				.top-right {
+					font-size: 16px;
+					.join_txt {
+						color: #409eff;
+						cursor: pointer;
+						&:hover {
+							text-decoration: underline;
+						}
+					}
+					.collected {
+						color: #f00;
+						cursor: pointer;
+						&:hover {
+							text-decoration: underline;
+						}
+					}
+					.span-item {
+						color: #409eff;
+						cursor: pointer;
+						&:hover {
+							text-decoration: underline;
+						}
+						.el-icon-collection,
+						.el-icon-document-copy {
+							color: #409eff;
+						}
+					}
+				}
+			}
+			.cont-bottom {
+				padding: 20px 30px;
+				/* height: calc(100vh - 250px);
+				overflow: auto; */
+				.el-input__inner {
+					height: 27px;
+					line-height: 27px;
+					padding: 0 4px;
+				}
+				.el-input-number .el-input__inner {
+					padding: 0 34px 0 4px;
+				}
+				.highcharts-range-selector-group {
+					display: none;
+					.highcharts-input-group {
+						display: none;
+					}
+				}
+				.highcharts-axis-title {
+					display: block;
+				}
+				/* =================== */
+				.chart-show-cont {
+					min-height: 300px;
+					padding: 0 150px 60px 120px;
+					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: 20px;
+						right: 50px;
+					}
+					.chartWrapper {
+						position: relative;
+						.range-cont {
+							position: absolute;
+							top: 15%;
+							.min-data-input {
+								width: 60px;
+								display: block;
+							}
+							&.left {
+								left: -80px;
+							}
+							&.right {
+								right: -65px;
+							}
+							&.rightTwo {
+								right: -130px;
+							}
+							&.bottom {
+								width: 100%;
+								display: flex;
+								justify-content: space-between;
+								top: auto;
+								right: 0;
+								bottom: -2%;
+								.left {
+									width: 60px;
+									display: block;
+									flex-shrink: 0;
+								}
+							}
+						}
+					}
+				}
+				.options-cont {
+					display: flex;
+					flex-wrap: wrap;
+					justify-content: space-between;
+				}
+			}
+
+		}
+    .nodata {
+      height: calc(100vh - 120px);
+			background-color: #fff;
+      text-align: center;
+      font-size: 16px;
+      color: #666;
+			padding: 100px 0;
+    }
+		
+		@media screen and (min-width: 1711px){
+			.min-data-input {
+				margin-top: 310px;
+			}
+			.btn-sty {
+				padding: 10px;
+			}
+			.year-btn,.btn-sty {
+				font-size: $font-normal;
+			}
+			.date-setting{
+				width: 210px;
+				height: 40px;
+				padding: 12px;
+				font-size: $font-normal;
+			}
+		}
+
+		@media screen and (max-width: 1710px){
+			.min-data-input {
+				margin-top: 230px;
+			}
+			.year-btn {
+				font-size: $font-small;
+				margin-left: 5px;
+				margin-right: 5px;
+				margin-bottom: 5px;
+				padding: 6px 12px;
+			}
+			.btn-sty {
+				font-size: $font-small;
+				margin-left: 5px;
+				padding: 6px;
+				border: 1px solid #409eff;
+			}
+			.date-setting{
+				width: 180px;
+				height: 36px;
+				padding: 10px;
+				font-size: $font-small;
+			}
+		}
+  }
+}
+</style>

+ 127 - 0
src/views/intervalAnalysis/components/saveChart.vue

@@ -0,0 +1,127 @@
+<template>
+  <el-dialog
+    :modal-append-to-body="false"
+    title="保存图"
+    :visible.sync="isShow"
+    :close-on-click-modal="false"
+    @close="cancelHandle"
+    center
+    v-dialogDrag
+    width="550px"
+  >
+    <div class="save-chart-wrap">
+      <el-form ref="diaForm" :model="formData">
+        <el-form-item :label="$t('Chart.Detail.chart_name')" prop="name">
+          <el-input
+            :placeholder="$t('Chart.Detail.chart_name')"
+            v-model="formData.name"
+            style="width: 400px"
+          ></el-input>
+        </el-form-item>
+        <el-form-item
+          :label="$t('Chart.Detail.chart_classify')"
+          prop="classify"
+        >
+          <el-cascader
+            v-model="formData.classify"
+            :options="classifyOpts"
+            :props="classifyProps"
+            clearable
+            :placeholder="$t('Chart.InputHolderAll.input_classify')"
+            style="width: 400px"
+          />
+        </el-form-item>
+      </el-form>
+
+      <div class="dia-bot">
+        <el-button type="primary" style="margin-right: 20px" @click="saveHandle"
+          ><!-- 保存 -->{{ $t("Dialog.confirm_save_btn") }}</el-button
+        >
+        <el-button type="primary" plain @click="cancelHandle"
+          ><!-- 取消 -->{{ $t("Dialog.cancel_btn") }}</el-button
+        >
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import apiIntervalAnalysis from '@/api/modules/intervalAnalysis'
+export default {
+  props: {
+    isShow: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      rules: {
+        name: [
+          { required: true, message: this.$t('StatisticAnalysis.CrossVarietyChart.fill_chart_name'), trigger: 'blur' }
+        ],
+        classify: [
+          { required: true, message: this.$t('MsgPrompt.select_category'), trigger: 'change' }
+        ],
+      },
+      formData: {
+        name: '',
+        classify: ''
+      },
+      classifyOpts: [],
+      classifyProps: {
+        label: 'ChartClassifyName',
+        value: 'ChartClassifyId',
+        children: 'Children',
+        checkStrictly: true,
+        emitPath:false
+      }
+
+    }
+  },
+  created() {
+    this.getClassifyOpts()
+  },
+  methods: {
+    async saveHandle(){
+      await this.$refs.diaForm.validate();
+      this.$emit('saveChart', this.formData)
+    },
+
+    cancelHandle(){
+      this.$emit("close");
+    },
+
+    filterNodesAll(arr) {
+      arr.length && arr.forEach(item => {
+        item.Children && item.Children.length && this.filterNodesAll(item.Children)
+        if (!item.Children.length) {
+          delete item.Children
+        }
+      })
+      return arr
+    },
+
+    getClassifyOpts() {
+      apiIntervalAnalysis.classifyListNoChart().then(res => {
+        if (res.Ret === 200) {
+          const arr = res.Data.AllNodes || []
+          this.classifyOpts = this.filterNodesAll(arr)
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.save-chart-wrap {
+  .el-input {
+    width: 100%;
+  }
+  .dia-bot{
+    margin: 30px 0;
+    text-align: center;
+  }
+}
+</style>

+ 670 - 0
src/views/intervalAnalysis/components/singleEdbWrap.vue

@@ -0,0 +1,670 @@
+<template>
+  <div class="single-edb-wrap">
+    <div class="search-cont">
+      <div>
+        <label><!-- 选择指标 -->{{ $t("Edb.choose_edb") }}:</label>
+        <el-radio-group v-model="edbFromType">
+          <el-radio :label="0" style="margin-right: 15px"
+            ><!-- ETA指标 -->{{ $t("Edb.eta_name") }}</el-radio
+          >
+          <el-radio :label="1"
+            ><!-- ETA预测指标 -->{{ $t("Edb.eta_predictor_name") }}</el-radio
+          >
+        </el-radio-group>
+      </div>
+      <el-select
+        v-model="edbInfoId"
+        v-loadMore="searchLoad"
+        ref="searchRef"
+        :filterable="!edbInfoId"
+        remote
+        clearable
+        :placeholder="$t('Edb.InputHolderAll.input_name_orid')"
+        style="width: 92%; margin-top: 10px; display: block"
+        :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.EdbInfoId"
+          :label="
+            chart_lang === 'en' ? item.EdbNameEn || item.EdbName : item.EdbName
+          "
+          :value="item.EdbInfoId"
+          :disabled="!item.HaveOperaAuth"
+        >
+          <edbDetailPopover :info="item">
+            <div slot="reference">
+              <img
+                :src="$icons.lock_ico2"
+                width="18"
+                height="18"
+                style="vertical-align: middle"
+                v-if="!item.HaveOperaAuth"
+              />
+              {{
+                chart_lang === "en"
+                  ? item.EdbNameEn || item.EdbName
+                  : item.EdbName
+              }}
+            </div>
+          </edbDetailPopover>
+        </el-option>
+      </el-select>
+    </div>
+    <!-- 区间划分 -->
+    <div class="item-box">
+      <span class="label">区间划分</span>
+      <div class="con-box">
+        <el-select v-model="dateRangeType" style="width: 90%">
+          <el-option
+            v-for="item in dateRangeTypeOpts"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          ></el-option>
+        </el-select>
+      </div>
+    </div>
+    <!-- 智能划分模块 -->
+    <template v-if="dateRangeType === 0">
+      <!-- 起始日期 -->
+      <div class="item-box" style="align-items: flex-start">
+        <span class="label" style="position: relative; top: 10px"
+          >起始日期</span
+        >
+        <div class="con-box">
+          <el-radio-group v-model="startDateConfig.startDateType">
+            <el-radio :label="0">
+              <span>固定</span>
+              <el-date-picker
+                style="width: 72%; margin-left: 10px"
+                v-model="startDateConfig.startDateVal"
+                value-format="yyyy-MM-dd"
+                popper-class="x-range-picker-date"
+                format="yyyy-MM-dd"
+                :placeholder="$t('Common.ph_time_start')"
+                :clearable="false"
+              ></el-date-picker>
+            </el-radio>
+            <el-radio :label="1" style="margin-top: 20px">
+              <span>动态</span>
+              <el-tooltip>
+                <div
+                  slot="content"
+                  v-html="$t('EtaChartAddPage.xserie_range_tip')"
+                />
+                <span style="cursor: pointer; margin-left: 10px">
+                  <span style="color: #333">说明</span>
+                  <img
+                    src="~@/assets/img/icons/question_mark_black.png"
+                    style="height: 16px; position: relative; top: 3px"
+                  />
+                </span>
+              </el-tooltip>
+            </el-radio>
+          </el-radio-group>
+        </div>
+      </div>
+      <!-- 起始日期动态模块 -->
+      <div v-show="startDateConfig.startDateType == 1">
+        <!-- 基准日期 -->
+        <div class="item-box" style="align-items: flex-start">
+          <span class="label" style="position: relative; top: 10px"
+            >基准日期</span
+          >
+          <div class="con-box">
+            <el-radio-group v-model="startDateConfig.baseDateType">
+              <el-radio :label="0">
+                <span>指标日期</span>
+                <span style="color: #333">{{
+                  $t("ETableChildren.lagging_period_label")
+                }}</span>
+                <el-input
+                  v-model="startDateConfig.MoveForward"
+                  type="number"
+                  :min="0"
+                  style="margin-right: 10px; width: 70px"
+                  @change="
+                    (e) => {
+                      startDateConfig.MoveForward = Number(e);
+                    }
+                  "
+                />
+                <span style="color: #333">{{
+                  $t("ETableChildren.term_ipt")
+                }}</span>
+              </el-radio>
+              <el-radio :label="1" style="margin-top: 20px">
+                <span>系统日期</span>
+              </el-radio>
+            </el-radio-group>
+          </div>
+        </div>
+        <dateMoveWaySec ref="startDateMoveWaySec" class="date-trans-box" />
+      </div>
+      <!-- 生成指标截止时间 -->
+      <div class="item-box" style="flex-wrap: wrap">
+        <span class="label">生成指标截止时间</span>
+        <div class="con-box" style="width: 100%">
+          <div>
+            <el-radio-group v-model="endDateConfig.baseDateType">
+              <el-radio :label="0">
+                <span>指标最新日期</span>
+                <span style="color: #333">{{
+                  $t("ETableChildren.lagging_period_label")
+                }}</span>
+                <el-input
+                  v-model="endDateConfig.MoveForward"
+                  type="number"
+                  :min="0"
+                  style="margin-right: 10px; width: 70px"
+                  @change="
+                    (e) => {
+                      endDateConfig.MoveForward = Number(e);
+                    }
+                  "
+                />
+                <span style="color: #333">{{
+                  $t("ETableChildren.term_ipt")
+                }}</span>
+
+                <dateMoveWaySec ref="endDateMoveWaySec" class="date-trans-box" />
+              </el-radio>
+              <el-radio :label="2" style="margin-top: 20px">
+                <span>固定</span>
+                <el-date-picker
+                  style="width: 72%; margin-left: 10px"
+                  v-model="endDateConfig.endDateVal"
+                  value-format="yyyy-MM-dd"
+                  popper-class="x-range-picker-date"
+                  format="yyyy-MM-dd"
+                  :placeholder="$t('Common.ph_time_start')"
+                  :clearable="false"
+                ></el-date-picker>
+              </el-radio>
+            </el-radio-group>
+          </div>
+        </div>
+      </div>
+    </template>
+    <!-- 手工划分模块 -->
+    <template v-if="dateRangeType === 1">
+      <!-- 时间段列表 -->
+      <div class="item-box" v-for="(item, index) in dateList" :key="index">
+        <span class="label">时间段{{ index + 1 }}</span>
+        <div class="con-box">
+          <el-date-picker
+            v-model="item.date"
+            type="daterange"
+            range-separator="至"
+            start-placeholder="起始日期"
+            end-placeholder="截至日期"
+            format="yyyy-MM-dd"
+            value-format="yyyy-MM-dd"
+            style="width: 90%"
+          />
+        </div>
+      </div>
+      <!-- 添加更多 -->
+      <div
+        style="
+          display: flex;
+          align-items: center;
+          color: #0052d9;
+          cursor: pointer;
+          margin: 20px 0;
+        "
+        @click="handleDateListAdd"
+      >
+        <img
+          style="width: 18px; margin-right: 5px"
+          src="~@/assets/img/add-quadrate-blue.png"
+          alt=""
+        />
+        <span style="font-size: 16px">添加更多</span>
+      </div>
+    </template>
+
+    <!-- 跨年划分模块 -->
+    <template v-if="dateRangeType === 2">
+      <div class="item-box">
+        <span class="label">前一年&nbsp;&nbsp;&nbsp;</span>
+        <div class="con-box">
+          <el-date-picker
+            v-model="yearDateStart"
+            format="MM-dd"
+            value-format="MM-dd"
+            popper-class="x-range-picker-date"
+            style="width: 90%"
+          />
+        </div>
+      </div>
+      <div class="item-box">
+        <span class="label">后一年&nbsp;&nbsp;&nbsp;</span>
+        <div class="con-box">
+          <el-date-picker
+            v-model="yearDateEnd"
+            format="MM-dd"
+            value-format="MM-dd"
+            popper-class="x-range-picker-date"
+            style="width: 90%"
+          />
+        </div>
+      </div>
+    </template>
+
+    <!-- 计算公式 -->
+    <div class="item-box" style="align-items: flex-start">
+      <span class="label" style="position: relative; top: 10px">计算公式</span>
+      <div class="con-box">
+        <el-select v-model="calculateType" style="width: 90%">
+          <el-option
+            v-for="item in calculateTypeOpts"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          ></el-option>
+        </el-select>
+      </div>
+    </div>
+
+    <!-- 原指标异常值处理 -->
+    <div class="item-box">
+      <div class="con-box">
+        <el-checkbox v-model="unNormalDataDeal.isDeal"
+          >原指标异常值处理</el-checkbox
+        >
+        <div v-show="unNormalDataDeal.isDeal">
+          <el-radio-group v-model="unNormalDataDeal.type">
+            <el-radio :label="1" style="margin-top: 10px">
+              <span style="color: #333">剔除</span>
+              <el-select
+                v-model="unNormalDataDeal.formula_out"
+                style="width: 60px"
+              >
+                <el-option
+                  v-for="item in symbolOpts"
+                  :key="item"
+                  :label="item"
+                  :value="item"
+                ></el-option>
+              </el-select>
+              <el-input
+                v-model="unNormalDataDeal.value_out"
+                type="number"
+                :min="0"
+                style="margin-right: 10px; width: 60px"
+                @change="
+                  (e) => {
+                    unNormalDataDeal.value_out = Number(e);
+                  }
+                "
+              />
+              <span style="color: #333">的值</span>
+            </el-radio>
+            <el-radio :label="2" style="margin-top: 10px">
+              <span style="color: #333">将&nbsp;&nbsp;&nbsp;&nbsp;</span>
+              <el-select
+                v-model="unNormalDataDeal.formula_replace"
+                style="width: 60px"
+              >
+                <el-option
+                  v-for="item in symbolOpts"
+                  :key="item"
+                  :label="item"
+                  :value="item"
+                ></el-option>
+              </el-select>
+              <el-input
+                v-model="unNormalDataDeal.value_replace"
+                type="number"
+                :min="0"
+                style="margin-right: 10px; width: 60px"
+                @change="
+                  (e) => {
+                    unNormalDataDeal.value_replace = Number(e);
+                  }
+                "
+              />
+              <span style="color: #333">的值,替换为</span>
+              <el-input
+                v-model="unNormalDataDeal.endValue"
+                type="number"
+                :min="0"
+                style="margin-right: 10px; width: 60px"
+                @change="
+                  (e) => {
+                    unNormalDataDeal.endValue = Number(e);
+                  }
+                "
+              />
+            </el-radio>
+          </el-radio-group>
+        </div>
+      </div>
+    </div>
+
+    <!-- 计算结果数据转换 -->
+    <div class="item-box">
+      <div class="con-box">
+        <el-checkbox v-model="dataConvert.isConvert"
+          >计算结果数据转换</el-checkbox
+        >
+        <div v-show="dataConvert.isConvert">
+          <div style="margin-top: 10px">
+            <el-select v-model="dataConvert.convertType" style="width: 80px">
+              <el-option
+                v-for="item in mathSymbolOpts"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+            <el-input
+              v-model="dataConvert.value"
+              type="number"
+              :min="0"
+              style="width: 120px"
+              @change="
+                (e) => {
+                  dataConvert.value = Number(e);
+                }
+              "
+            />
+          </div>
+          <div style="margin-top: 10px">
+            <span style="display: inline-block; width: 80px">单位</span>
+            <el-input v-model="dataConvert.unit" style="width: 60px" />
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { dataBaseInterface } from '@/api/api.js';
+import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js';
+import dateMoveWaySec from '@/views/datasheet_manage/components/dateMoveWaySection.vue'
+
+export default {
+  components: { dateMoveWaySec },
+  computed: {
+    // 区间划分类型选项数据
+    dateRangeTypeOpts() {
+      return [
+        {
+          label: '智能划分',
+          value: 0
+        },
+        {
+          label: '手工划分',
+          value: 1
+        },
+        {
+          label: '跨年划分',
+          value: 2
+        }
+      ]
+    },
+    calculateTypeOpts() {
+      return [
+        {
+          label: '区间均值',
+          value: 0
+        },
+        {
+          label: '区间累计值',
+          value: 1
+        },
+        {
+          label: '区间涨幅',
+          value: 2
+        },
+        {
+          label: '区间年化增长率',
+          value: 3
+        },
+        {
+          label: '区间最大值',
+          value: 4
+        },
+        {
+          label: '区间最小值',
+          value: 5
+        }
+      ]
+    },
+    mathSymbolOpts() {
+      return [
+        {
+          label: '乘以',
+          value: 1,
+        },
+        {
+          label: '除以',
+          value: 2,
+        },
+        {
+          label: '对数',
+          value: 3,
+        }
+      ]
+    }
+  },
+  data() {
+    return {
+      edbFromType: 0,//指标数据来源 eta 和eta预测
+      searchOptions: [],
+      search_have_more: true,
+      search_page: 1,
+      current_search: '',
+      edbInfoId:'',
+
+      dateRangeType: 0,//区间划分 0:智能划分,1:手工划分,2:跨年划分
+
+      startDateConfig: {
+        startDateType: 0,//起始日期类型 0固定 1动态
+        startDateVal: '2020-01-01',
+        baseDateType: 0,//基准日期类型:0指标日期,1系统日期,2固定日期
+        MoveForward: 0,
+        dateTransfData: [],//日期变换数据
+      },
+
+      endDateConfig: {
+        baseDateType: 0,//准日期类型:0指标日期,1系统日期,2固定日期
+        endDateVal: '2020-01-01',
+        MoveForward: 0,
+        dateTransfData: [],//日期变换数据
+      },
+
+      calculateType: 0,//计算类型 0: 区间均值,1: 区间累计值,2:区间涨幅,3:区间年化增长率,4:区间最大值,5:区间最小值
+
+      symbolOpts: ['=', '>', '<', '>=', '<='],
+      unNormalDataDeal: {//原指标异常值处理
+        isDeal: false,
+        type: 1,//1:剔除,2替换
+        formula_out: '=',
+        value_out: 0,
+        formula_replace: '=',
+        value_replace: 0,
+        endValue: 0,
+      },
+      dataConvert: {
+        isConvert: false,
+        convertType: 1,//0不转, 1乘 2除 3对数
+        value: 100,
+        unit: '',
+      },
+
+      dateList: [
+        { date: '' }
+      ],//时间段
+
+      yearDateStart: '',//跨年划分前一年
+      yearDateEnd: '',//跨年划分后一年
+
+
+
+    }
+  },
+  methods: {
+    // 
+    getParams() {
+      this.startDateConfig.dateTransfData=this.$refs.startDateMoveWaySec.dateChangeArr||[]
+      this.endDateConfig.dateTransfData=this.$refs.endDateMoveWaySec.dateChangeArr||[]
+
+      const params = {
+        ChartEdbInfoList: [
+          {
+            EdbInfoId: this.edbInfoId||0,
+            IsAxis: 0,
+            EdbAliasName: '',
+          }
+        ],
+
+        ExtraConfig:{
+          EdbInfoMode: 0,//指标模式 0: 单指标,1: 多指标
+          DateRangeType: this.dateRangeType,
+          AutoDateConf: {//智能划分数据
+            IsAutoStartDate: this.startDateConfig.startDateType,
+            StartDate: this.startDateConfig.startDateVal,
+            StartDateConf: {
+              BaseDateType: this.startDateConfig.baseDateType,
+              MoveForward: this.startDateConfig.MoveForward,
+              DateChange: this.startDateConfig.dateTransfData || []
+            },
+            EndDateConf: {
+              BaseDateType: this.endDateConfig.baseDateType,
+              MoveForward: this.endDateConfig.MoveForward,
+              DateChange: this.endDateConfig.dateTransfData || []
+            }
+          },
+          //手工划分数据
+          ManualDateConf: this.dateList.filter(i => i.date).map(i => {
+            return {
+              StartDate: i.date[0],
+              EndDayte: i.date[1]
+            }
+          }),
+          YearDateConf: {//跨年划分
+            StartDay: this.yearDateStart,
+            EndDay: this.yearDateEnd
+          },
+          CalculateType: this.calculateType,
+          UnNormalDataDealType: this.unNormalDataDeal.isDeal === false ? 0 : this.unNormalDataDeal.type,
+          UnNormalDataConf: {
+            Formula: this.unNormalDataDeal.type === 1 ? this.unNormalDataDeal.formula_out : this.unNormalDataDeal.formula_replace,
+            Value: this.unNormalDataDeal.type === 1 ? this.unNormalDataDeal.value_out : this.unNormalDataDeal.value_replace,
+            ReplaceValue: this.unNormalDataDeal.endValue,
+          },
+          DataConvertType: this.dataConvert.isConvert === false ? 0 : this.dataConvert.convertType,
+          DataConvertConf: {
+            Value: this.dataConvert.value,
+            Unit: this.dataConvert.unit
+          },
+        }
+      }
+
+      return params
+    },
+
+    // 添加更多时间段
+    handleDateListAdd() {
+      this.dateList.push({ date: '' })
+    },
+
+    /* 搜索 */
+    searchHandle(query) {
+      this.search_page = 1;
+      this.current_search = query;
+      this.searchApi(this.current_search)
+    },
+
+    async searchApi(query, page = 1) {
+      let params = {
+        KeyWord: query,
+        CurrentIndex: page,
+      }
+      const res = this.edbFromType === 0
+        ? await dataBaseInterface.targetSearchByPage(params)
+        : await preDictEdbInterface.edbSearch(params)
+
+      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);
+    },
+
+    /* 聚焦获取当前检索 */
+    inputFocusHandle(e) {
+      this.search_page = 1;
+      this.current_search = e.target.value;
+      this.searchApi(this.current_search);
+    },
+
+    searchLoad() {
+      if (!this.search_have_more) return;
+      this.searchApi(this.current_search, ++this.search_page);
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.single-edb-wrap {
+  margin-top: 20px;
+  .item-box {
+    margin-top: 20px;
+    display: flex;
+    align-items: center;
+    .label {
+      // width: 80px;
+      flex-shrink: 0;
+      margin-right: 10px;
+    }
+    .con-box {
+      flex: 1;
+    }
+  }
+}
+</style>
+<style lang="scss">
+/*他不要下拉选择框 QAQ */
+.x-range-picker-date {
+  display: none;
+}
+.single-edb-wrap {
+  .date-trans-box {
+    margin-top: 20px;
+    padding-left: 0 !important;
+    .header {
+      label {
+        width: auto !important;
+        text-align: left !important;
+        margin-left: 0 !important;
+      }
+    }
+    .date-change-list {
+      padding-left: 0 !important;
+      .date-change-li {
+        .date-item {
+          .el-input {
+            width: 50px !important;
+            .el-input__inner {
+              padding: 0 5px;
+            }
+          }
+          .el-select {
+            .el-input {
+              width: 80px !important;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 605 - 0
src/views/intervalAnalysis/list.vue

@@ -0,0 +1,605 @@
+<template>
+  <div class="fittingEquation-chart-container 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 type="primary" @click="goAddChart">{{
+            $t("StatisticAnalysis.ChartRelevance.chart_add_btn")
+          }}</el-button>
+        </div>
+
+        <div class="search-cont">
+          <el-checkbox
+            v-model="isShowMe"
+            @change="
+              () => {
+                getTreeData();
+                getPublicList();
+              }
+            "
+            >{{
+              $t("StatisticAnalysis.ChartRelevance.only_see_mine")
+            }}</el-checkbox
+          >
+          <el-select
+            v-model="search_txt"
+            v-loadMore="searchLoad"
+            :filterable="!search_txt"
+            remote
+            clearable
+            :placeholder="$t('Chart.search_chart_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="
+                currentLang === 'en'
+                  ? item.ChartNameEn || item.ChartName
+                  : item.ChartName
+              "
+              :value="item.ChartInfoId"
+            >
+            </el-option>
+          </el-select>
+        </div>
+        <div class="tree-cont">
+          <el-tree
+            class="target_tree"
+            ref="menuTree"
+            :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="暂无目录"
+            lazy
+            :load="getLazyTreeData"
+            @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 && isEdbBtnShow('editCatalog')"
+                @blur="changeValue(node, data)"
+              />
+              <span
+                @dblclick.stop="editNodeLabel(node, data)"
+                v-else
+                class="text_oneLine node_label"
+                :style="`width:${
+                  (select_node === data.UniqueCode && node.Nodewidth) || ''
+                }`"
+                :id="`node${data.UniqueCode}`"
+              >
+                <img
+                  :src="$icons.lock_ico2"
+                  width="18"
+                  height="18"
+                  style="vertical-align: middle"
+                  v-if="!data.HaveOperaAuth && data.EdbInfoId"
+                />
+                <span>{{
+                  currentLang === "en"
+                    ? data.ChartClassifyNameEn || data.ChartClassifyName
+                    : data.ChartClassifyName
+                }}</span>
+              </span>
+              <span
+                style="display: flex; align-items: center"
+                v-if="select_node === data.UniqueCode && data.HaveOperaAuth"
+              >
+                <!-- 添加子项 -->
+                <img
+                  src="~@/assets/img/set_m/add.png"
+                  alt=""
+                  style="width: 14px; height: 14px; margin-right: 8px"
+                  @click.stop="addNode(node, data)"
+                  v-if="
+                    data.Button.AddButton &&
+                    isEdbBtnShow('editCatalog') &&
+                    node.level < 6
+                  "
+                />
+                <!-- 编辑节点 如果是分类,判断data.Button.OpButton不变;如果是指标,不显示(ETA1.0.3) -->
+                <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.EdbCode &&
+                    data.Button.OpButton &&
+                    isEdbBtnShow('editCatalog')
+                  "
+                />
+                <!-- 删除节点 如果是分类,判断data.Button.DeleteButton不变;如果是指标,不显示(ETA1.0.3) -->
+                <img
+                  slot="reference"
+                  src="~@/assets/img/set_m/del.png"
+                  alt=""
+                  style="width: 14px; height: 14px"
+                  @click.stop="removeNode(node, data)"
+                  v-if="
+                    !data.EdbCode &&
+                    data.Button.DeleteButton &&
+                    isEdbBtnShow('deleteCatalog')
+                  "
+                />
+              </span>
+            </span>
+          </el-tree>
+          <div class="noDepart" @click="addLevelOneHandle">
+            <img
+              src="~@/assets/img/set_m/add_ico.png"
+              alt=""
+              style="width: 16px; height: 16px; margin-right: 10px"
+            />
+            <span>{{ $t("EtaBasePage.add_first_menu_btn") }}</span>
+          </div>
+        </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="chart-show-cont" v-if="!chartInfo.WarnMsg">
+                <div class="chartWrapper" id="chartWrapper">
+                  <h2 class="chart-title">
+                    {{
+                      currentLang === "en"
+                        ? chartInfo.ChartNameEn || chartInfo.ChartName
+                        : chartInfo.ChartName
+                    }}
+                  </h2>
+                  <Chart
+                    :options="options"
+                    :chartInfo="chartInfo"
+                    minHeight="440px"
+                    height="500px"
+                    ref="chartRef"
+                  />
+                  <template>
+                    <div class="range-cont left" v-if="leftIndex !== -1">
+                      <el-input
+                        style="width: 60px; display: block"
+                        size="mini"
+                        type="number"
+                        placeholder="上限"
+                        v-model="chartLimit.max"
+                        @change="changeLimit"
+                      />
+                      <el-input
+                        class="min-data-input"
+                        size="mini"
+                        type="number"
+                        placeholder="下限"
+                        v-model="chartLimit.min"
+                        @change="changeLimit"
+                      />
+                    </div>
+                    <div class="range-cont right" v-if="rightIndex !== -1">
+                      <el-input
+                        style="width: 60px; display: block"
+                        size="mini"
+                        type="number"
+                        placeholder="上限"
+                        v-model="chartLimit.rightMax"
+                        @change="changeLimit"
+                      />
+                      <el-input
+                        class="min-data-input"
+                        size="mini"
+                        type="number"
+                        placeholder="下限"
+                        v-model="chartLimit.rightMin"
+                        @change="changeLimit"
+                      />
+                    </div>
+                    <!-- 右2上下限设置 -->
+                    <div
+                      class="range-cont rightTwo"
+                      v-if="rightTwoIndex !== -1"
+                    >
+                      <el-input
+                        style="width: 60px; display: block"
+                        size="mini"
+                        type="number"
+                        placeholder="上限"
+                        v-model="chartLimit.rightTwoMax"
+                        @change="changeLimit"
+                      />
+                      <el-input
+                        class="min-data-input"
+                        size="mini"
+                        type="number"
+                        placeholder="下限"
+                        v-model="chartLimit.rightTwoMin"
+                        @change="changeLimit"
+                      />
+                    </div>
+                  </template>
+                </div>
+                <div class="chart-bottom-insruction-info">
+                  <div class="chart-source">
+                    <span
+                      v-if="chartInfo.SourcesFrom"
+                      :style="`
+                        color: ${
+                          JSON.parse(chartInfo.SourcesFrom).isShow
+                            ? JSON.parse(chartInfo.SourcesFrom).color
+                            : '#999'
+                        };
+                        fontSize: ${JSON.parse(chartInfo.SourcesFrom).fontSize}px;
+                      `"><!-- 来源 -->{{ $t("Chart.Detail.source") }}:{{
+                        JSON.parse(chartInfo.SourcesFrom).text
+                      }}
+                    </span>
+                  </div>
+
+                  <!-- 图表说明 -->
+                  <div
+                    class="chart-instruction"
+                    v-if="
+                      chartInfo.Instructions &&
+                      JSON.parse(chartInfo.Instructions).isShow
+                    "
+                    v-text="JSON.parse(chartInfo.Instructions).text"
+                    :style="`
+									color: ${JSON.parse(chartInfo.Instructions).color};
+									fontSize: ${JSON.parse(chartInfo.Instructions).fontSize}px
+								`"
+                  ></div>
+                </div>
+                <span class="chart-author"
+                  >{{ $t("MsgPrompt.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"
+                @openLangInfoDia="openLangInfoDia"
+                @delChartHandle="delChartHandle"
+              />
+            </el-col>
+          </el-row>
+        </div>
+        <!-- 列表 -->
+        <chartListTableWrap
+          v-else
+          :total="chart_total"
+          :list="chartList"
+          @refresh="handleChartRefresh"
+          @saveOther="handleSaveOther"
+          @deleteChart="handleDeleteChart"
+          @loadMoreHandle="loadMoreHandle"
+          @detailShowHandle="detailShowHandle"
+          @addMychartHandle="addMychartHandle"
+        />
+      </div>
+    </div>
+
+    <!-- 目录弹窗 -->
+    <openDialog
+      :isOpenDialog="isOpenDialog"
+      :title="dialog_title"
+      :formData="dialogForm"
+      @closeDia="isOpenDialog = false"
+      @sucessCallback="classifyCallback"
+    />
+
+    <!-- 图表另存 -->
+    <SaveChartOther
+      :show.sync="isShowSaveOther"
+      fromType=""
+      source="interval_analysis"
+      :data="saveOtherChartInfo"
+      @ensure="getTreeData"
+    />
+  </div>
+</template>
+
+<script>
+import apiIntervalAnalysis from '@/api/modules/intervalAnalysis'
+import leftMixin from './mixin/leftMixin';
+import Chart from '@/views/dataEntry_manage/components/chart';
+import openDialog from '@/views/dataEntry_manage/databaseComponents/openDialog';
+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 setLangInfoDia from '@/views/dataEntry_manage/components/setLangInfo.vue'
+export default {
+  components: {
+    openDialog,
+    Chart,
+    addMyClassifyDia,
+    SaveChartOther,
+    setEnNameDia,
+    setLangInfoDia
+  },
+  mixins: [leftMixin, chartSetMixin],
+  data() {
+    return {
+      showData: false,
+      refreshLoading: false,
+      isShowMe: false,
+      search_txt: '',
+      searchOptions: [],
+      isSlideLeft: false,//左侧分类收起
+
+      select_node: '',//节点唯一标识code
+      select_classify: '',
+
+      select_id: '',//选中的图表id
+      chartInfo: {},
+      chart_title: '',//图表标题 双击标题修改时来存储最新值
+
+      /* 图表列表 */
+      listLoading: false,
+      listFinished: true,//是否还有列表数据
+      chartList: [],
+      chart_total: 0,
+      chart_page: 1,
+      chart_pages_size: 30,
+
+      search_page: 1,
+      search_have_more: false,
+      current_search: '',
+
+      isShowSaveOther: false,//另存为
+      saveOtherChartInfo: {},
+
+    };
+  },
+  methods: {
+
+    /* 添加图表 */
+    goAddChart() {
+      if (!this.treeData.length) return this.$message.warning('请先添加分类');
+      this.$router.push({ path: '/rangeAnalysisChartEditor' });
+    },
+
+    /* 搜索 */
+    searchHandle(query) {
+      this.search_page = 1;
+      this.current_search = query;
+      this.searchApi(this.current_search)
+    },
+
+    searchApi(query, page = 1) {
+      /* 查找列表 */
+      apiIntervalAnalysis
+        .chartSearch({
+          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);
+    },
+
+    /* 获取图表列表 */
+    getPublicList() {
+      this.listLoading = true
+      apiIntervalAnalysis.getChartList({
+        CurrentIndex: this.chart_page,
+        PageSize: this.chart_pages_size,
+        ChartClassifyId: this.select_classify || 0,
+        IsShowMe: this.isShowMe
+      }).then(res => {
+        this.listLoading = false
+        if (res.Ret !== 200) return
+
+        this.listFinished = res.Data.Paging.IsEnd
+        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;
+      }).catch(() => {
+        this.listLoading = false
+      })
+    },
+
+    loadMoreHandle() {
+      if (this.listLoading || this.listFinished) return
+      this.chart_page++
+      this.getPublicList()
+    },
+
+    detailShowHandle(e){
+      this.select_id=e.ChartInfoId
+    },
+
+    handleInitList() {
+      this.listFinished = false
+      this.chart_page = 1
+      this.chartList = []
+      this.getPublicList()
+    },
+
+    getChartInfo() {
+      this.getDetailHandle();
+    },
+
+    /* 获取图表详情 */
+    getDetailHandle() {
+      apiIntervalAnalysis.chartInfo({
+        ChartInfoId: this.select_id
+      }).then(res => {
+        if (res.Ret !== 200) return
+
+        this.initIntervalAnalysisChartData(res.Data)
+        // 展开左侧目录树
+        this.defaultShowNodes=res.Data.ClassifyLevels||[]
+        //设置tree高亮
+				this.$nextTick(()=>{
+					setTimeout(() => {
+						this.$refs.menuTree.setCurrentKey(res.Data.ChartInfo.UniqueCode);
+					}, 1000);
+				})
+
+      })
+    },
+
+    /* 删除图表 */
+    handleDeleteChart(e) {
+      const { ChartClassifyId, ChartInfoId } = e;
+      this.$confirm(this.$t('Chart.OptMsg.chart_del_confirm'), this.$t('Confirm.prompt'), {
+        // confirmButtonText: '确定',
+        // cancelButtonText: '取消',
+        type: 'warning',
+      }).then(() => {
+        this.delClassify(ChartClassifyId, ChartInfoId, 'del_chart');
+      })
+        .catch(() => { });
+    },
+
+    /* 刷新图表 */
+    handleChartRefresh(e) {
+      this.refreshLoading = this.$loading({
+        lock: true,
+        target: '.main-right',
+        text: /* '刷新图表中...' */this.$t('Chart.OptMsg.refresh_ing_msg'),
+        spinner: 'el-icon-loading',
+        background: 'rgba(255, 255, 255, 0.8)',
+      });
+      apiIntervalAnalysis.chartRefresh({
+        ChartInfoId: e.ChartInfoId,
+      }).then((res) => {
+        this.refreshLoading.close();
+        if (res.Ret === 200) {
+          this.$message.success(res.Msg);
+        }
+      });
+    },
+
+    // 显示另存为
+    handleSaveOther(e) {
+      this.saveOtherChartInfo = e
+      this.isShowSaveOther = true
+    },
+
+    /* 编辑图表 */
+    editChartHandle() {
+      this.$router.push({
+        path: '/rangeAnalysisChartEditor',
+        query: {
+          code: this.chartInfo.UniqueCode,
+          id: this.chartInfo.ChartInfoId
+        }
+      })
+    },
+  },
+  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 "~@/views/chartRelevance_manage/css/index.scss";
+.pub-chart-box .data-sheet-main .main-left {
+  width: 400px;
+}
+</style>
+
+<style lang="scss">
+@import "~@/views/chartRelevance_manage/css/pub.scss";
+</style>

+ 511 - 0
src/views/intervalAnalysis/mixin/leftMixin.js

@@ -0,0 +1,511 @@
+import apiIntervalAnalysis from '@/api/modules/intervalAnalysis'
+
+export default {
+  data() {
+    return {
+      treeData: [], //分类数据
+      defaultProp: {
+        label: 'ChartClassifyName',
+				children: 'Children',
+				isLeaf:'isLeaf'
+      }, //树结构配置项
+      defaultShowNodes: [], //展开节点
+      new_label: '',//双击修改的value
+      dynamicNode: null,
+
+      /* 新增编辑目录弹窗 */
+			isOpenDialog:false,
+			dialog_title: '',
+			dialogForm:{
+				level:''
+			},
+
+    };
+  },
+  watch: {
+    /* 设置动态右侧区域宽度 */
+    isSlideLeft(newval) {
+      this.$nextTick(() => {
+        this.reloadRightWid();
+      });
+    },
+    /* 图表id */
+    select_id(newval) {
+      if (newval) {
+        // this.currentLang = "ch";
+        this.getDetailHandle();
+      } else {
+        this.chartInfo = {};
+      }
+    },
+
+    select_classify(newval) {
+      if (this.$refs.chartListWrap) this.$refs.chartListWrap.$refs.listRef.scrollTop = 0;
+      if (newval) {
+        this.chart_page = 1;
+        this.getPublicList();
+      }
+    },
+
+    /* 搜索关键词 */
+    search_txt(newval) {
+      if (newval) {
+        let search_obj = this.searchOptions.find(
+          (_) => _.ChartInfoId === newval
+        );
+      }
+    },
+  },
+  computed: {
+    role() {
+			let role = localStorage.getItem('Role') || '';
+			if (['rai_researcher', 'ficc_researcher', 'researcher','ficc_seller','rai_seller','seller'].includes(role)) {
+				return 'researcher'
+			}else if(['rai_admin','ficc_admin'].includes(role)) {
+				return 'admin'
+			}
+			else {
+				return role;
+			}
+		},
+  },
+  directives: {
+    drag(el, bindings) {
+      el.onmousedown = function(e) {
+				var init = e.clientX;
+				var box = $("#box")[0]
+				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;
+					if(end > 310){
+            var newWidth = end - init + initWidth;
+						// right.style.width = total_wid-end+80 +'px'
+						right.style.width = total_wid - newWidth + 'px'
+            left.style.width = newWidth + "px";
+          }else{
+            end = 350;
+            // 最小宽度300
+            left.style.width = 300 + "px";
+						// right.style.width = total_wid-300-20 +'px'
+          }
+        };
+        document.onmouseup = function() {
+          document.onmousemove = document.onmouseup = null;
+					e.releaseCapture && e.releaseCapture();
+        };
+				e.setCapture && e.setCapture();
+				return false;
+      };
+    }
+  },
+
+  methods: {
+    //控制页面按钮权限
+		isEdbBtnShow(type){
+			const {edbDataPermission,checkPermissionBtn}=this.permissionBtn
+			const BtnMap = {
+				'update':edbDataPermission.edbData_update,//指标更新
+				'edit':edbDataPermission.edbData_edit,//指标编辑
+				/* 'toImg':edbDataPermission.edbData_toImgs, */
+				'copyData':edbDataPermission.edbData_copyData,//复制数据
+				'toSource':edbDataPermission.edbData_edbSource,//指标溯源
+				'setEn':edbDataPermission.edbData_enNameSetting,//设置英文名称
+				'newValue':edbDataPermission.edbData_newestValue,//添加/编辑最新值
+				'refreshAll':edbDataPermission.edbData_refreshAll,//全部刷新
+				'saveEdb':edbDataPermission.edbData_saveEdb,//保存指标
+				'deleteEdb':edbDataPermission.edbData_deleteEdb,//删除指标
+				'showChartBasis':edbDataPermission.edbData_showChartBasis,//展示/隐藏同比图
+				'switchSeason':edbDataPermission.edbData_switchSeason,//切换季节性图
+				'editLimit':edbDataPermission.edbData_editLimit,//编辑上下限
+				'calculateAgain':edbDataPermission.edbData_calculateAgain,//重新计算
+
+				'editCatalog':edbDataPermission.edbData_classifyOpt_add,//添加编辑目录
+				'deleteCatalog':edbDataPermission.edbData_classifyOpt_delete,//删除目录
+				'moveCatalog':edbDataPermission.edbData_classifyOpt_move,//删除目录
+				'checkRelatedChart':edbDataPermission.edbData_checkRelatedChart,//查看关联图表
+				'checkRelatedEdb':edbDataPermission.edbData_checkRelatedEdb,//查看关联指标
+				'checkCalcChart':edbDataPermission.edbData_checkCalcChart,//查看计算指标
+				'enableOrDisable':edbDataPermission.edbData_enableOrDisable,//启用/停用
+			}
+			return checkPermissionBtn(BtnMap[type])
+		},
+
+
+    /* 根据unicode展开树结构并选中当前图表 重置图表配置 日期区间 */
+    selectCurrentNode({ code, id, type }) {
+      
+    },
+
+    // 递归节点
+		getNodeParentData(data,arr){
+			if(data.level===0) return
+			arr.push({
+        classifyName:this.currentLang==='en'?data.data.ChartClassifyNameEn:data.data.ChartClassifyName,
+        classifyId:data.data.ChartClassifyId
+      })
+			this.getNodeParentData(data.parent,arr)
+			return arr
+		},
+
+    /* 添加一级目录 */
+    addLevelOneHandle() {
+      this.dialog_title = '添加';
+      this.dialogForm = {
+				level_1: '',
+				parent_id: '',
+				level: 0
+			}
+			this.isOpenDialog = true;
+    },
+
+    /* 添加节点 */
+		addNode(node,data) {
+			console.log(node);
+			this.dialog_title = '添加';
+			let arr=[]
+			arr=this.getNodeParentData(node,arr)
+			// console.log(arr);
+			
+				/* 添加目录 */
+			this.dialogForm = {
+				parentArr:arr,
+				parent_id: data.ChartClassifyId,
+				level: node.level,
+				levelVal:'',
+				isEDB:false
+			}
+			//存储当前要新增子级的目录code
+			sessionStorage.setItem('expandCode', data.UniqueCode);
+			this.isOpenDialog = true;
+		},
+
+    /* 编辑节点 */
+    editNode(node, { ChartClassifyName, ChartClassifyNameEn, ChartClassifyId }) {
+
+      this.dialog_title = '编辑';
+      let arr=[]
+			arr=this.getNodeParentData(node.parent,arr)
+			/* 编辑目录 */
+			this.dialogForm = {
+				isEDB:false,
+				parentArr:arr,
+				levelVal: this.currentLang==='en'?ChartClassifyNameEn:ChartClassifyName,
+				classify_id: ChartClassifyId,
+				level: node.level-1
+			}
+			this.isOpenDialog = true;
+    },
+
+    /* 分类成功回调 */
+    classifyCallback(type) {
+      this.isOpenDialog = false;
+      this.getTreeData();
+
+      if(type === 'add') {
+				//新增分类完成之后,展开父节点显示刚新增的分类,若已展开节点则不做处理
+				let code = sessionStorage.getItem('expandCode');
+				let flag = 	this.defaultShowNodes.some((item) => {
+					return item === code
+				});
+				// console.log(flag)
+				!flag &&code&& this.defaultShowNodes.push(code);
+				sessionStorage.removeItem('expandCode');
+			}
+    },
+
+    /* 删除节点校验 */
+    async removeNode(node, { ChartClassifyId, ChartInfoId }) {
+
+      const { Data } = await apiIntervalAnalysis.classifyDelCheck({ ChartClassifyId })
+
+      const { DeleteStatus } = Data;
+      
+      // DeleteStatus:0,可删除,进行删除操作,1该分类下关联图表不可删除,2确认删除当前目录及包含的子目录吗
+      if(DeleteStatus === 1){
+        this.$confirm(this.$t('Chart.OptMsg.classify_del_fail'), this.$t('Chart.OptMsg.del_fail_tag'), {
+          confirmButtonText: this.$t('MsgPrompt.known'),
+          showCancelButton: false,
+          type: 'error',
+        })
+      }else if(DeleteStatus === 0){
+        this.delClassify(ChartClassifyId, ChartInfoId)
+      }else if(DeleteStatus === 2){
+        this.$confirm(this.$t('Chart.OptMsg.classify_delall_confirm'), this.$t('Confirm.prompt'), {
+            type: 'warning',
+          }).then(() => {
+            this.delClassify(ChartClassifyId, ChartInfoId)
+          })
+      }
+
+    },
+
+    // 目录上的删除
+    delClassify(ChartClassifyId, ChartInfoId,type){
+      apiIntervalAnalysis.classifyDelete({
+				ChartClassifyId,
+				ChartInfoId
+			}).then(res=>{
+        if(res.Ret === 200) {
+					this.$message.success(this.$t('MsgPrompt.delete_msg'));
+					this.getTreeData();
+					this.select_id = 0;
+					if(type==='del_chart'){
+						this.handleInitList()
+					}
+				}
+      })
+    },
+
+    /* 获取分类 */
+    getTreeData(params = null) {
+      apiIntervalAnalysis.classifyList({ IsShowMe: this.isShowMe,ParentId:0 }).then(res => {
+        const { Ret, Data } = res;
+        if (Ret !== 200) return
+        const arr=Data.AllNodes || []
+        this.treeData = arr;
+        this.showData = true;
+
+        this.$nextTick(() => {
+          /* 新增完成后 处理树展开和选中 */
+          params && this.selectCurrentNode(params);
+        });
+
+      })
+    },
+
+    //绑定el-tree的load属性
+		async getLazyTreeData (node,resolve){
+			if(node.level===0){
+				resolve(this.treeData)
+			}else{
+				let arr=[]
+				const res=await apiIntervalAnalysis.classifyList({ParentId:node.data.ChartClassifyId,IsOnlyMe:this.IsOnlyMe})
+				if (res.Ret === 200) {
+					const temarr = res.Data.AllNodes || [];
+					arr=temarr.map(item=>{
+						return {
+							...item,
+							isLeaf:item.ChartInfoId?true:false
+						}
+					})
+				}
+				resolve(arr)
+			}
+		},
+
+    /* 选中分类变化时 */
+    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;
+    },
+
+    /* 拖动时node宽度跟随变化 */
+    resetNodeStyle: _.debounce(function(node) {
+			const tree = $('.tree-cont')[0];
+			let width = tree.offsetWidth;
+			let label_wid =
+				width > 500
+					? 'auto'
+					: width <= 260
+					? 80
+					: 0.4 * width;
+			this.$set(node, 'Nodewidth', label_wid + 'px');
+		},200),
+
+    /* 双击label出现input修改框 */
+		editNodeLabel(node, data) {
+			//目录名称可以双击修改 指标不能
+			if(!data.EdbCode && this.role === 'admin'&&this.isEdbBtnShow('editCatalog')) {
+				this.$set(data,'isEdit',true)
+				this.new_label = this.currentLang==='en' ? data.ChartClassifyNameEn : data.ChartClassifyName;
+				this.$nextTick(() => {
+					this.$refs.editVal.focus();
+				});
+			}
+		},
+
+    /* input失去焦点恢复node 修改最新的值*/
+		async changeValue(node,data) {
+      if(!this.new_label) return this.$message.warning('名称不能为空');
+      this.$set(data,'isEdit',false)
+
+      if((this.new_label===data.ChartClassifyName&&this.currentLang==='zh')||(this.new_label===data.ChartClassifyNameEn&&this.currentLang==='en')) return 
+      
+      let res=await apiIntervalAnalysis.classifyEdit({
+        ChartClassifyId: data.ChartClassifyId,
+        ChartClassifyName: this.new_label
+      });
+
+      if(res.Ret !== 200) return
+
+      this.getTreeData();
+    },
+
+    /* 拖拽完成 */
+    dropOverHandle(b,a,i,e) {
+			// 被拖拽节点对应的 Node、结束拖拽时最后进入的节点、被拖拽节点的放置位置
+			console.log(b,a,i);
+			const isEDB=b.data.ChartInfoId?true:false
+			let list=a.parent.childNodes;
+			let targetIndex=0,PrevClassifyId=0,NextClassifyId=0,ParentClassifyId=0;
+			let ClassifyId=0,ChartInfoId=0,PrevChartInfoId=0,NextChartInfoId=0;
+
+			ClassifyId=isEDB?0:b.data.ChartClassifyId
+			ChartInfoId=isEDB?b.data.ChartInfoId:0
+			
+
+			if(i!=='inner'){
+				ParentClassifyId=a.parent.data.ChartClassifyId||0
+				list.forEach((item,index)=>{
+					if(isEDB){
+						if(item.data.ChartInfoId===b.data.ChartInfoId){
+							targetIndex=index
+						}
+					}else{
+						if(item.data.ChartClassifyId===b.data.ChartClassifyId){
+							targetIndex=index
+						}
+					}
+					
+				})
+
+				console.log(targetIndex);
+				
+				
+				if(targetIndex===0){
+					const data=list[targetIndex+1].data
+					NextClassifyId=data.ChartInfoId?0:data.ChartClassifyId
+					NextChartInfoId=data.ChartInfoId?data.ChartInfoId:0
+				}else if(targetIndex===list.length-1){
+					const data=list[targetIndex-1].data
+					PrevClassifyId=data.ChartInfoId?0:data.ChartClassifyId
+					PrevChartInfoId=data.ChartInfoId?data.ChartInfoId:0
+				}else{
+					const pData=list[targetIndex-1].data
+					PrevClassifyId=pData.ChartInfoId?0:pData.ChartClassifyId
+
+					PrevChartInfoId=pData.ChartInfoId?pData.ChartInfoId:0
+
+					const nData=list[targetIndex+1].data
+					NextClassifyId=nData.ChartInfoId?0:nData.ChartClassifyId
+					NextChartInfoId=nData.ChartInfoId?nData.ChartInfoId:0
+				}
+			}else{
+				ParentClassifyId=a.data.ChartClassifyId||0
+			}
+
+			const params={
+				ClassifyId,
+				ParentClassifyId,
+				ChartInfoId,
+				PrevClassifyId,
+				NextClassifyId,
+				PrevChartInfoId,
+				NextChartInfoId
+			}
+			console.log(params);
+			apiIntervalAnalysis.classifyMoveSort(params).then(res=>{
+				if(res.Ret===200){
+					// this.$message.success('移动成功!')
+					this.$message.success(this.$t('MsgPrompt.move_sort_success'))
+				}
+				this.getTreeData()
+				if(this.selected_edbid){
+					this.getDataList();
+				}
+				
+			})
+		},
+
+    /* 拖拽覆盖添加背景色 */
+    dropMouseOver(node1, node2, e) {
+      if(!node2.data.ChartInfoId&&(node1.level>node2.level||(node1.data.ChartInfoId>0&&!node2.data.ChartInfoId)) && (e.target.childNodes[0].className.includes('el-tree-node__content') 
+			|| e.target.className.includes('el-tree-node__content'))) {
+				// console.log(e.target.childNodes[0])
+				e.target.childNodes[0].className.includes('el-tree-node__content') 
+				? e.target.childNodes[0].style.backgroundColor = '#409eff' 
+				: e.target.style.backgroundColor = '#409eff';
+			}
+    },
+
+    /* 拖拽离开/拖拽完成重置背景色 */
+    dropMouseLeave(node1, node2, e) {
+      let arrs = $(".el-tree-node__content");
+      for (let a of arrs) {
+        a.style.backgroundColor = "transparent";
+      }
+    },
+
+    // 树节点展开
+    handleNodeExpand(data) {
+      // 保存当前展开的节点
+      let flag = this.defaultShowNodes.some((item) => item === data.UniqueCode);
+
+      if (!flag) {
+        // 不存在则存到数组里
+        this.defaultShowNodes.push(data.UniqueCode);
+      }
+    },
+
+    // 树节点关闭
+    handleNodeCollapse(data) {
+      this.defaultShowNodes.some((item, index) => {
+        if (item === data.UniqueCode) {
+          // 删除关闭节点
+          this.defaultShowNodes.length = index;
+        }
+      });
+    },
+
+    /* 判断节点是否能被拖拽 */
+    canDragHandle({data}) {
+      return data.Button.MoveButton;
+    },
+
+    /* 判断节点是否能被拖入 */
+    canDropHandle(draggingNode, dropNode, type) {
+      let canDrop=false
+			
+			// 如果拖动的是指标
+			if(draggingNode.data.ChartInfoId){
+				if(!(dropNode.level===1&&type!=='inner')){
+					canDrop=true
+				}
+			}else{//拖动的是目录
+				// console.log(dropNode.level,draggingNode.level);
+				//目录层级不能改变
+				if((dropNode.level+1==draggingNode.level&&type==='inner'&&!dropNode.data.ChartInfoId)||(dropNode.level===draggingNode.level&&type!=='inner')){
+					canDrop=true
+				}
+			}
+			return canDrop
+    },
+
+    /* 加载更多 */
+		loadMoreHandle: _.throttle(function() {
+			let scrollTop = this.$refs.chartListWrap.$refs.listRef.scrollTop;
+      let clientHeight = this.$refs.chartListWrap.$refs.listRef.clientHeight;
+      let scrollHeight = this.$refs.chartListWrap.$refs.listRef.scrollHeight;
+			if(scrollTop + clientHeight >= scrollHeight-10 && this.publicHaveMove){
+				this.chart_page++;
+				this.getPublicList();
+			}
+		},300),
+
+    /* 重绘右侧区域宽度 */
+    reloadRightWid() {
+      let total_wid = $('.data-sheet-main')[0].offsetWidth;
+      let left = $('#left')[0].offsetWidth;
+      let rigtWid = total_wid - left - 20 + 'px';
+      $('#right')[0].style.width = rigtWid;
+
+    },
+  },
+};