Browse Source

Merge branch 'master' of http://8.136.199.33:3000/eta_front/eta_mobile_front

jwyu 1 năm trước cách đây
mục cha
commit
b3aa9b7e5e

+ 282 - 12
src/views/dataEDB/calculate/components/FormulaCalculate.vue

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

+ 4 - 2
src/views/dataEDB/util/config.js

@@ -21,8 +21,10 @@ export const edbUnitOpts=[
 
 //计算类型说明tips
 export const calculateTypeTipsMap=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>

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

@@ -1,5 +1,6 @@
 <script setup>
 import {ref,computed,watch} from 'vue'
+import { showToast } from 'vant';
 import {_apiLogin} from '@/api/user'
 import {useLogin} from './hooks/useLogin'
 const props = defineProps({
@@ -29,15 +30,19 @@ let emailCode = ref('')
 const isEmailCountDown = computed(()=>{
     return codeCountDown.value<60&&codeCountDown.value>0
 })
+let isGetNewCode = ref(false)
 
 const emailPattern = /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/
 function checkForm(){
     if(isEmailCountDown.value) return 
     //是否是重新获取验证码
-    if(codeStr.value==='重新获取'){
+    if(codeStr.value==='重新获取'&&!isGetNewCode.value){
         //清空图形验证码输入框,引导用户重新输入
+        showToast("重新获取需再次输入图形验证码")
         getPicCode()
         emailPicCode.value = ''
+        isGetNewCode.value = true
+        return
     }
     //检查邮箱和图形验证码是否正确
     if(!emailPicCode.value.length){
@@ -57,6 +62,7 @@ function checkForm(){
         email:email.value,
         picCode:emailPicCode.value
     },'email',1)
+    isGetNewCode.value = false
 }
 </script>
 

+ 7 - 1
src/views/login/MobileModel.vue

@@ -38,6 +38,8 @@ const isMobileCountDown = computed(()=>{
     return codeCountDown.value<60&&codeCountDown.value>0
 })
 
+let isGetNewCode = ref(false)
+
 //区号相关
 let mobileTelAreaCode=ref('86')
 let showMobilePicker = ref(false)
@@ -55,10 +57,13 @@ getTelAreaCode()
 function checkForm(){
     if(isMobileCountDown.value) return 
     //是否是重新获取验证码
-    if(codeStr.value==='重新获取'){
+    if(codeStr.value==='重新获取'&&!isGetNewCode.value){
         //清空图形验证码输入框,引导用户重新输入
+        showToast("重新获取需再次输入图形验证码")
         getPicCode()
         mobilePicCode.value = ''
+        isGetNewCode.value = true
+        return
     }
     //检查手机号和图形验证码是否正确
     if(!mobilePicCode.value.length){
@@ -77,6 +82,7 @@ function checkForm(){
               areaCode:mobileTelAreaCode.value,
               picCode:mobilePicCode.value
             },'mobile',1)
+    isGetNewCode.value = false
 
 }
 

+ 1 - 0
src/views/login/hooks/useLogin.js

@@ -29,6 +29,7 @@ export function useLogin(){
         if(res.Ret!==200) return 
         showToast('验证码已发送')
         //60秒倒计时
+        codeCountDown.value = 60
         countDown()
         timer = setInterval(()=>{
             countDown()