|
@@ -1,7 +1,7 @@
|
|
|
<script setup>
|
|
|
import apiDataEDB from '@/api/dataEDB'
|
|
|
import { showToast } from 'vant';
|
|
|
-import {reactive, ref,watch} from 'vue'
|
|
|
+import {computed, reactive, ref,watch} from 'vue'
|
|
|
import SelectEDB from './SelectEDB.vue'
|
|
|
import EDBHistory from '@/views/dataEDB/components/EDBHistory.vue'
|
|
|
import SelectEDBClassify from '../../components/SelectEDBClassify.vue'
|
|
@@ -10,6 +10,8 @@ import SelectEDBFrequency from '../../components/SelectEDBFrequency.vue'
|
|
|
import {calculateTypeTipsMap} from '../../util/config'
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
|
|
+import SelectDate from '@/components/SelectDate.vue'
|
|
|
+
|
|
|
const route=useRoute()
|
|
|
const router=useRouter()
|
|
|
|
|
@@ -34,11 +36,20 @@ watch(
|
|
|
name:item.FromEdbName
|
|
|
}
|
|
|
})
|
|
|
- formulaVal.value=props.edbInfo.EdbInfoDetail.CalculateFormula
|
|
|
+ formulaList.value= JSON.parse(props.edbInfo.EdbInfoDetail.CalculateFormula).map(_ =>({
|
|
|
+ formula: _.f,
|
|
|
+ date: _.d
|
|
|
+ }))
|
|
|
baseInfo.name=props.edbInfo.EdbInfoDetail.EdbName
|
|
|
baseInfo.unit=props.edbInfo.EdbInfoDetail.Unit
|
|
|
baseInfo.classify=props.edbInfo.EdbInfoDetail.ClassifyId
|
|
|
baseInfo.frequency=props.edbInfo.EdbInfoDetail.Frequency
|
|
|
+ baseInfo.nullValueWay=props.edbInfo.EdbInfoDetail.EmptyType
|
|
|
+ baseInfo.maxNullWay=props.edbInfo.EdbInfoDetail.MaxEmptyType
|
|
|
+
|
|
|
+ nullWayStr.value = nullWayOptions.value.find(_ =>_.value===baseInfo.nullValueWay).label
|
|
|
+ maxNullStr.value = maxNullWayOptions.value.find(_=>_.value===baseInfo.maxNullWay).label
|
|
|
+
|
|
|
setTimeout(() => {
|
|
|
selectEDBClassifyINS.value?.getSelectClassifyOpt(props.edbInfo.EdbInfoDetail.ClassifyId)//获取选择的分类目录
|
|
|
}, 1000);
|
|
@@ -59,6 +70,7 @@ initLetterOpt()
|
|
|
|
|
|
//公式说明
|
|
|
const showTips=ref(false)
|
|
|
+const tips = ref('')
|
|
|
const tipsContent=ref(calculateTypeTipsMap.get(Number(route.query.source))||'')
|
|
|
|
|
|
//选择的指标集合
|
|
@@ -160,7 +172,9 @@ const baseInfo=reactive({
|
|
|
name:'',
|
|
|
unit:'',
|
|
|
classify:'',
|
|
|
- frequency:''
|
|
|
+ frequency:'',
|
|
|
+ nullValueWay: 1,
|
|
|
+ maxNullWay: 1
|
|
|
})
|
|
|
|
|
|
// 选择单位
|
|
@@ -190,9 +204,108 @@ function handleConfirmFrequency(value){
|
|
|
baseInfo.frequency=value
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/* 公式改为列表 */
|
|
|
+const formulaList = ref([{ formula: '' }])
|
|
|
+const showSelectFormulaDate = ref(false)
|
|
|
+const selectDateItem = ref({})
|
|
|
+//日期时间段
|
|
|
+const formulaDateArr = computed(() => {
|
|
|
+ return formulaList.value.map(_ => _.date).filter(_ => _).sort((a,b) => new Date(a)-new Date(b))
|
|
|
+})
|
|
|
+/* 新增公式分段 */
|
|
|
+function addFormulaHandle() {
|
|
|
+ let addItem = {
|
|
|
+ formula: formulaList.value[formulaList.value.length-1].formula,
|
|
|
+ date: ''
|
|
|
+ }
|
|
|
+ formulaList.value.push(addItem)
|
|
|
+}
|
|
|
+/* 移除公式 */
|
|
|
+function removeFormulaItem(index) {
|
|
|
+ formulaList.value.splice(index,1)
|
|
|
+}
|
|
|
+/* 选择日期 检验 */
|
|
|
+function handleConfirmFormulaDate(val) {
|
|
|
+ const { startDate,endDate } = edbList.value[0];
|
|
|
+
|
|
|
+ if(!startDate) {
|
|
|
+ selectDateItem.value.date = val;
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let dateStamp = new Date(val).getTime(),
|
|
|
+ startStamp = new Date(startDate).getTime(),
|
|
|
+ endStamp = new Date(endDate).getTime();
|
|
|
+ if (dateStamp > endStamp || dateStamp < startStamp) {
|
|
|
+ return showToast('分段日期必须在第一个指标日期区间')
|
|
|
+ }
|
|
|
+
|
|
|
+ else if(formulaList.value.filter(_ => _.date===val).length>1) {
|
|
|
+ return showToast('分段日期不可重复')
|
|
|
+ }
|
|
|
+
|
|
|
+ selectDateItem.value.date = val;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+//空值处理弹窗
|
|
|
+const nullWayStr = ref('查找前后35天最近值')
|
|
|
+const showNullValPoup = ref(false)
|
|
|
+const nullWayOptions = ref([
|
|
|
+ { label: '查找前后35天最近值',value: 0 },
|
|
|
+ { label: '不计算',value: 1 },
|
|
|
+ { label: '前值填充',value: 2 },
|
|
|
+ { label: '后值填充',value: 3 },
|
|
|
+ { label: '等于0',value: 4 },
|
|
|
+])
|
|
|
+function onConfirmNullWay(e) {
|
|
|
+ baseInfo.nullValueWay = e.selectedValues[0]
|
|
|
+ nullWayStr.value = e.selectedOptions[0].label
|
|
|
+ showNullValPoup.value = false
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* max空值处理显示 当输入的公式包含MAX、MIN且空值处理为0时,输入公式失焦后出现右侧选项; */
|
|
|
+const showMaxNullDeal = computed(()=> {
|
|
|
+ let haveMaxOrMin = formulaList.value.some(_ => _.formula.toUpperCase().includes('MAX') || _.formula.toUpperCase().includes('MIN'))
|
|
|
+
|
|
|
+ return haveMaxOrMin && baseInfo.nullValueWay===4
|
|
|
+})
|
|
|
+//max空值处理
|
|
|
+const maxNullStr = ref('等于0')
|
|
|
+const showMaxNullValPoup = ref(false)
|
|
|
+const maxNullWayOptions = ref([
|
|
|
+ { label: '等于0',value: 1 },
|
|
|
+ { label: '跳过空值',value: 2 },
|
|
|
+])
|
|
|
+function onConfirmMaxNullWay(e) {
|
|
|
+ baseInfo.maxNullWay = e.selectedValues[0]
|
|
|
+ maxNullStr.value = e.selectedOptions[0].label
|
|
|
+ showMaxNullValPoup.value = false
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+//一些文案提示
|
|
|
+const formTips = ref({
|
|
|
+ '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、分段时间节点不允许重复,不允许超出第一个指标的日期区间`
|
|
|
+})
|
|
|
+
|
|
|
// 提交计算
|
|
|
async function handleSave(){
|
|
|
- if(!formulaVal.value){
|
|
|
+ if(!formulaList.value[0].formula){
|
|
|
showToast('计算公式不能为空')
|
|
|
return
|
|
|
}
|
|
@@ -218,13 +331,21 @@ async function handleSave(){
|
|
|
FromTag: item.tag,
|
|
|
}
|
|
|
})
|
|
|
+
|
|
|
+ let CalculateFormula = JSON.stringify(formulaList.value
|
|
|
+ .filter((_,index) => index===0||(index>0&&_.formula&&_.date))
|
|
|
+ .map(_ => ({ f:_.formula,d: _.date }))
|
|
|
+ )
|
|
|
+
|
|
|
const params={
|
|
|
- CalculateFormula:formulaVal.value,
|
|
|
+ CalculateFormula,
|
|
|
ClassifyId:baseInfo.classify,
|
|
|
EdbName:baseInfo.name,
|
|
|
Frequency:baseInfo.frequency,
|
|
|
Unit:baseInfo.unit,
|
|
|
- EdbInfoIdArr:arr
|
|
|
+ EdbInfoIdArr:arr,
|
|
|
+ EmptyType:baseInfo.nullValueWay,
|
|
|
+ MaxEmptyType:baseInfo.maxNullWay
|
|
|
}
|
|
|
const edbInfoId=route.query.edbInfoId
|
|
|
const res=edbInfoId?await apiDataEDB.editCalculateFormula({...params,EdbInfoId:Number(edbInfoId)}):await apiDataEDB.addCalculateFormula(params)
|
|
@@ -282,7 +403,90 @@ async function handleSave(){
|
|
|
</div>
|
|
|
</section>
|
|
|
<section class="section formula-box">
|
|
|
- <van-field
|
|
|
+
|
|
|
+ <div class="section">
|
|
|
+ <van-field
|
|
|
+ v-model="nullWayStr"
|
|
|
+ required
|
|
|
+ input-align="right"
|
|
|
+ readonly
|
|
|
+ @click-input="showNullValPoup = true"
|
|
|
+ :right-icon="!isPreview?'arrow':''"
|
|
|
+ :disabled="isPreview"
|
|
|
+ >
|
|
|
+ <template #label>
|
|
|
+ <span>
|
|
|
+ 空值处理
|
|
|
+ <svg-icon class="icon" name="warning" @click="showTips=true;tips=formTips['null-val']"></svg-icon>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </van-field>
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ v-model="maxNullStr"
|
|
|
+ required
|
|
|
+ input-align="right"
|
|
|
+ readonly
|
|
|
+ @click-input="showMaxNullValPoup = true"
|
|
|
+ :right-icon="!isPreview?'arrow':''"
|
|
|
+ :disabled="isPreview"
|
|
|
+ v-if="showMaxNullDeal"
|
|
|
+ >
|
|
|
+ <template #label>
|
|
|
+ <span>
|
|
|
+ MAX、MIN空值处理
|
|
|
+ <svg-icon class="icon" name="warning" @click="showTips=true;tips=formTips['max-null-val']"></svg-icon>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </van-field>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="van-cell formula-wrap">
|
|
|
+ <label class="van-cell__title van-field__label--required">计算公式
|
|
|
+ <svg-icon class="icon" name="warning" @click="showTips=true;tips=formTips['formula']"></svg-icon>
|
|
|
+ </label>
|
|
|
+ <ul class="formula-list">
|
|
|
+
|
|
|
+ <li class="formula-item" v-for="(item,index) in formulaList" :key="index">
|
|
|
+ <template v-if="formulaDateArr.length&&(item.date||!index)">
|
|
|
+ <span v-if="index===0" class="date-section-text">{{formulaDateArr[formulaDateArr.length-1]}}(含)之后</span>
|
|
|
+ <span v-else-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>
|
|
|
+
|
|
|
+ <van-field
|
|
|
+ v-model="item.formula"
|
|
|
+ placeholder="输入公式"
|
|
|
+ required
|
|
|
+ :disabled="isPreview"
|
|
|
+ class="formula-input"
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-button type="default" size="small" v-if="index===0&&!isPreview" @click="addFormulaHandle" style="margin-left: 10px;">添加分段</van-button>
|
|
|
+
|
|
|
+ <template v-else>
|
|
|
+ <van-field
|
|
|
+ v-model="item.date"
|
|
|
+ placeholder="日期"
|
|
|
+ required
|
|
|
+ :disabled="isPreview"
|
|
|
+ readonly
|
|
|
+ class="formula-input"
|
|
|
+ style="margin: 0 20px;"
|
|
|
+ @click-input="showSelectFormulaDate=true;selectDateItem=item;"
|
|
|
+ />
|
|
|
+
|
|
|
+ <van-icon name="clear" color="#333" size="20" @click="removeFormulaItem(index)" v-if="!isPreview"/>
|
|
|
+ </template>
|
|
|
+ </li>
|
|
|
+ <li class="formula-tips">
|
|
|
+ <p class="en-text-wrap">公式示例:A*0.5+B*C*1.2+120-MAX(A,B,C)</p>
|
|
|
+ <p class="en-text-wrap">函数支持:MAX(),MIN(),ln(A),log(a,A),abs(),exp(),pow(),round()</p>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ <!-- <van-field
|
|
|
label="计算公式"
|
|
|
required
|
|
|
>
|
|
@@ -295,7 +499,7 @@ async function handleSave(){
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
- </van-field>
|
|
|
+ </van-field> -->
|
|
|
</section>
|
|
|
<section class="section baseinfo-box">
|
|
|
<van-field
|
|
@@ -346,7 +550,7 @@ async function handleSave(){
|
|
|
:disabled="isPreview"
|
|
|
/>
|
|
|
</section>
|
|
|
- <div class="formula-intro-btn" @click="showTips=true">
|
|
|
+ <div class="formula-intro-btn" @click="showTips=true;tips=tipsContent">
|
|
|
<svg-icon class="icon" name="warning"></svg-icon>
|
|
|
<span>公式说明</span>
|
|
|
</div>
|
|
@@ -371,14 +575,55 @@ async function handleSave(){
|
|
|
<!-- 选择频度 -->
|
|
|
<SelectEDBFrequency v-model:show="showSelectFrequency" @select="handleConfirmFrequency"/>
|
|
|
|
|
|
+ <!-- 选择日期 -->
|
|
|
+ <SelectDate v-model:show="showSelectFormulaDate" @select="handleConfirmFormulaDate"/>
|
|
|
+
|
|
|
+
|
|
|
<!-- 公式说明 -->
|
|
|
<van-dialog
|
|
|
- v-model:show="showTips"
|
|
|
- :title="$route.query.name"
|
|
|
+ v-model:show="showTips"
|
|
|
confirmButtonText='知道啦'
|
|
|
>
|
|
|
- <div class="edb-formula-tips-html-box" v-html="tipsContent"></div>
|
|
|
+ <div class="edb-formula-tips-html-box" v-html="tips"></div>
|
|
|
</van-dialog>
|
|
|
+
|
|
|
+ <!-- 空值处理方式 -->
|
|
|
+ <van-popup
|
|
|
+ :show="showNullValPoup"
|
|
|
+ round
|
|
|
+ position="bottom"
|
|
|
+ >
|
|
|
+ <van-picker
|
|
|
+ title=""
|
|
|
+ :columns="nullWayOptions"
|
|
|
+ :columns-field-names="{
|
|
|
+ text: 'label',
|
|
|
+ value: 'value'
|
|
|
+ }"
|
|
|
+ @cancel="showNullValPoup=false"
|
|
|
+ @confirm="onConfirmNullWay"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <!-- MAX空值处理方式 -->
|
|
|
+ <van-popup
|
|
|
+ :show="showMaxNullValPoup"
|
|
|
+ round
|
|
|
+ position="bottom"
|
|
|
+ >
|
|
|
+ <van-picker
|
|
|
+ title=""
|
|
|
+ :columns="maxNullWayOptions"
|
|
|
+ :columns-field-names="{
|
|
|
+ text: 'label',
|
|
|
+ value: 'value'
|
|
|
+ }"
|
|
|
+ @cancel="showMaxNullValPoup=false"
|
|
|
+ @confirm="onConfirmMaxNullWay"
|
|
|
+ />
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+
|
|
|
</template>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
@@ -438,6 +683,7 @@ async function handleSave(){
|
|
|
border-radius: 12px;
|
|
|
line-height: 1.7;
|
|
|
width: 100%;
|
|
|
+ flex: 1;
|
|
|
}
|
|
|
.formula-tips{
|
|
|
font-size: 24px;
|
|
@@ -483,6 +729,20 @@ async function handleSave(){
|
|
|
max-width: 300PX;
|
|
|
}
|
|
|
}
|
|
|
+.formula-wrap {
|
|
|
+ display: block;
|
|
|
+ .formula-list { margin-top: 15px; }
|
|
|
+ .formula-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ position: relative;
|
|
|
+ padding: 20px 0;
|
|
|
+ .date-section-text {
|
|
|
+ position: absolute;
|
|
|
+ top: -15px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
@media screen and (min-width:$media-width){
|
|
|
.formula-calculate-wrap{
|
|
|
padding-bottom: 105px;
|
|
@@ -539,6 +799,16 @@ async function handleSave(){
|
|
|
justify-content: center;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
+ .formula-wrap .formula-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ position: relative;
|
|
|
+ padding: 15px 0;
|
|
|
+ .date-section-text {
|
|
|
+ position: absolute;
|
|
|
+ top: -10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
</style>
|