Pārlūkot izejas kodu

Merge branch 'eta1.2.0'

Karsa 11 mēneši atpakaļ
vecāks
revīzija
7dcb7910cd

+ 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;
 			}
 		}
 	}

+ 4 - 2
src/views/dataEntry_manage/databaseComponents/util.js

@@ -230,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>

+ 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  {

+ 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  {