Jay
2024-09-24 67ea852973d664ab98c128412f7528e13038f34b
指标定义
已添加1个文件
436 ■■■■■ 文件已修改
src/views/data/ind/item/CalIndDefineForm.vue 436 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/ind/item/CalIndDefineForm.vue
对比新文件
@@ -0,0 +1,436 @@
<template>
  <Dialog v-model="dialogVisible" :title="dialogTitle" width="55%">
    <el-form
      ref="formRef"
      v-loading="formLoading"
      :model="formData"
      :rules="formRules" label-width="100px">
      <el-row>
        <el-col :span="12">
          <el-form-item label="指标编码" prop="itemNo">
            <el-input v-model="formData.itemNo" disabled/>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="指标名称" prop="itemName">
            <el-input v-model="formData.itemName" placeholder="请输入指标名称"/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="指标分类" prop="itemCategory">
            <el-select v-model="formData.itemCategory" clearable placeholder="请选择指标分类">
              <el-option
                v-for="item in dataCategoryList"
                :key="item.id"
                :label="item.label"
                :value="item.id + ''"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="时间粒度" prop="timeGranularity">
            <el-select v-model="formData.timeGranularity" placeholder="请选择">
              <el-option
                v-for="dict in getStrDictOptions(DICT_TYPE.TIME_GRANULARITY)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
              />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="8">
          <el-form-item label="指标精度" prop="precision">
            <el-input v-model="formData.precision"/>
          </el-form-item>
        </el-col>
        <el-col :span="8">
          <el-form-item label="转换系数" prop="coefficient">
            <el-input v-model="formData.coefficient"/>
          </el-form-item>
        </el-col>
        <el-col :span="8">
          <el-form-item label="数量单位" prop="unit">
            <el-input v-model="formData.unit"/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="24">
          <el-form-item label="备注" prop="remark">
            <el-input v-model="formData.remark" type="textarea" maxlength="100"/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="24">
          <el-form-item label="表达式">
            <el-table :data="expressionList" border style="width: 100%">
              <el-table-column type="index" align="center" width="60" label="序号"/>
              <el-table-column prop="" label="左括号" width="100" align="center">
                <template #default="scope">
                  <el-input size="small" v-model="scope.row.parenthesesLeft" placeholder="" readonly maxlength="10"/>
                  <el-button size="small" @click="addParenthesesLeft(scope.$index, scope.row)">+</el-button>
                  <el-button size="small" @click="removeParenthesesLeft(scope.$index, scope.row)">-</el-button>
                </template>
              </el-table-column>
              <el-table-column prop="" label="指标" align="center">
                <template #default="scope">
                  <el-select size="mini" v-model="scope.row.itemNo" filterable placeholder="请选择">
                    <el-option v-for="(item, index) in itemList"
                               :key="index"
                               :label="item.itemName"
                               :value="item.itemNo"/>
                  </el-select>
                </template>
              </el-table-column>
              <el-table-column prop="" label="运算值" align="center">
                <template #default="scope">
                  <el-input size="mini" v-model="scope.row.itemNo" placeholder="运算值" clearable/>
                </template>
              </el-table-column>
              <el-table-column prop="" label="右括号" width="100" align="center">
                <template #default="scope">
                  <el-input size="small" v-model="scope.row.parenthesesRight" placeholder="" readonly/>
                    <el-button size="small" @click="addParenthesesRight(scope.$index, scope.row, ')')">+
                    </el-button>
                    <el-button size="small" @click="removeParenthesesRight(scope.$index, scope.row)">-
                    </el-button>
                </template>
              </el-table-column>
              <el-table-column prop="" label="运算符" width="100" align="center">
                <template #default="scope">
                  <el-select size="mini" v-model="scope.row.operator" clearable placeholder="请选择"
                             style="font-weight: 600;">
                    <el-option v-for="item in operatorList"
                               :key="item"
                               :label="item"
                               :value="item"/>
                  </el-select>
                </template>
              </el-table-column>
              <el-table-column prop="" label="操作" width="100" align="center">
                <template #default="scope">
                  <el-button @click="addExpressionRow(scope.$index, expressionList)" link type="primary" size="small">添加</el-button>
                  <el-button @click="deleteExpressionRow(scope.$index, expressionList)" link type="danger" size="small">删除</el-button>
                </template>
              </el-table-column>
            </el-table>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <template #footer>
      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
      <el-button @click="dialogVisible = false">取 消</el-button>
    </template>
  </Dialog>
</template>
<script lang="ts" setup>
  import {DICT_TYPE, getStrDictOptions} from '@/utils/dict'
  import * as DataSetApi from '@/api/data/ind/data/data.set'
  import {CommonStatusEnum} from '@/utils/constants'
  import * as DataSourceConfigApi from "@/api/infra/dataSourceConfig";
  import * as ItemApi from '@/api/data/ind/item/item'
  import { ElMessage } from 'element-plus'
  import * as CategoryApi from '@/api/data/ind/category/index'
  defineOptions({name: 'IndDataSetForm'})
  const {t} = useI18n() // 国际化
  const message = useMessage() // 消息弹窗
  const dialogVisible = ref(false) // 弹窗的是否展示
  const dialogTitle = ref('') // 弹窗的标题
  const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  const formType = ref('') // 表单的类型:create - 新增;update - 修改
  const itemList = ref([] as ItemApi.ItemVO[])
  let formData = ref({
    id: undefined,
    itemNo: '',
    itemName: '',
    itemType: '',
    itemCategory: '',
    coefficient: '',
    precision: '',
    businessType: '',
    timeRange: '',
    timeGranularity: '',
    remark: '',
    calItem: {
      id: '',
      expression: '',
    }
  })
  let expressionList = ref([{
    parenthesesLeft: '',
    itemNo: '',
    parenthesesRight: '',
    operator: ''
  }])
  const validateAsNumber = (rule, value, callback) => {
    const regex = /^(\-|\+)?\d+(\.\d+)?$/;
    if (!regex.test(value)) {
      callback(new Error('请输入数字!'));
    }
  }
  const operatorList = ref(['+', '-', '*', '/', '&', '|', '!', '>', '<'])
  const formRules = reactive({
    itemName: [{required: true, message: '指标名称不能为空', trigger: 'blur'}],
    itemCategory: [{required: true, message: '指标类型不能为空', trigger: 'blur'}],
    precision: [{validator: validateAsNumber, trigger: 'blur' }],
    coefficient: [{validator: validateAsNumber, trigger: 'blur' }],
  })
  const formRef = ref() // 表单 Ref
  const dataSourceList = ref([] as DataSourceConfigApi.DataSourceConfigVO[])
  const queryParams = reactive({})
  const dataCategoryList = ref([] as CategoryApi.IndItemCategoryVO[])
  /** 打开弹窗 */
  const open = async (type: string, id?: number) => {
    dialogVisible.value = true
    dialogTitle.value = '复合指标'
    formType.value = type
    resetForm()
    // 加载数据源列表
    dataCategoryList.value = await CategoryApi.getCategoryListAllSimple()
    itemList.value = await ItemApi.getItemList(queryParams)
    // 修改时,设置数据
    if (id) {
      formLoading.value = true
      try {
        formData.value = await ItemApi.getItem(id)
        expressionList.value = []
        let expression = formData.value.calItem.expression
        do {
          let indexArray = [
            expression.indexOf('+'),
            expression.indexOf('-'),
            expression.indexOf('*'),
            expression.indexOf('/'),
            expression.indexOf('&'),
            expression.indexOf('|'),
            expression.indexOf('!'),
            expression.indexOf('>'),
            expression.indexOf('<')
          ].sort(numAscSort)
          if (indexArray[indexArray.length - 1] !== -1) {
            let endIndex = 0
            for (let key in indexArray) {
              if (indexArray[key] > -1) {
                endIndex = indexArray[key]
                break
              }
            }
            // 运算值
            let itemNoStr = expression.substring(0, endIndex)
            // 运算符
            let operator = expression.substr(endIndex, 1)
            let indexOfParenthesesLeft = itemNoStr.indexOf('(')
            let lastIndexOfParenthesesLeft = itemNoStr.lastIndexOf('(')
            // 左括号
            let parenthesesLeft = ''
            if (indexOfParenthesesLeft !== -1 && lastIndexOfParenthesesLeft !== -1) {
              parenthesesLeft = itemNoStr.substring(indexOfParenthesesLeft, lastIndexOfParenthesesLeft + 1)
              itemNoStr = itemNoStr.substring(lastIndexOfParenthesesLeft + 1)
            }
            let indexOfParenthesesRight = itemNoStr.indexOf(')')
            let lastIndexOfParenthesesRight = itemNoStr.lastIndexOf(')')
            // 右括号
            let parenthesesRight = ''
            if (indexOfParenthesesRight !== -1 && lastIndexOfParenthesesRight !== -1) {
              parenthesesRight = itemNoStr.substring(indexOfParenthesesRight, lastIndexOfParenthesesRight + 1)
              itemNoStr = itemNoStr.substring(0, indexOfParenthesesRight)
            }
            expressionList.value.push({
              parenthesesLeft: parenthesesLeft,
              itemNo: itemNoStr,
              parenthesesRight: parenthesesRight,
              operator: operator
            })
            expression = expression.substring(endIndex + 1)
          } else {
            let pointStr = expression
            let indexOfParenthesesLeft = pointStr.indexOf('(')
            let lastIndexOfParenthesesLeft = pointStr.lastIndexOf('(')
            let parenthesesLeft = ''
            if (indexOfParenthesesLeft !== -1 && lastIndexOfParenthesesLeft !== -1) {
              parenthesesLeft = pointStr.substring(indexOfParenthesesLeft, lastIndexOfParenthesesLeft + 1)
              pointStr = pointStr.substring(lastIndexOfParenthesesLeft + 1)
            }
            let indexOfParenthesesRight = pointStr.indexOf(')')
            let lastIndexOfParenthesesRight = pointStr.lastIndexOf(')')
            let parenthesesRight = ''
            if (indexOfParenthesesRight !== -1 && lastIndexOfParenthesesRight !== -1) {
              parenthesesRight = pointStr.substring(indexOfParenthesesRight, lastIndexOfParenthesesRight + 1)
              pointStr = pointStr.substring(0, indexOfParenthesesRight)
            }
            expressionList.value.push({
              parenthesesLeft: parenthesesLeft,
              itemNo: pointStr,
              parenthesesRight: parenthesesRight,
              operator: ''
            })
            expression = ''
          }
        } while (expression && expression.length > 0)
      } finally {
        formLoading.value = false
      }
    }
  }
  function numAscSort(a, b) {
    return a - b
  }
  defineExpose({open}) // 提供 open 方法,用于打开弹窗
  /** 提交表单 */
  const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
  const submitForm = async () => {
    // 校验表单
    if (!formRef) return
    const valid = await formRef.value.validate()
    if (!valid) return
    // 提交请求
    formLoading.value = true
    try {
      if (expressionList.value && expressionList.value.length > 0) {
        let parenthesesLeftRex = /^[(]*$/
        let parenthesesRightRex = /^[)]*$/
        let expression = ''
        for (let i = 0; i < expressionList.value.length; i++) {
          let value = expressionList.value[i]
          if (!parenthesesLeftRex.test(value.parenthesesLeft)) {
            ElMessage({
              message: `第${i + 1}行左括号输入不正确!`,
              type: 'error',
              duration: 1500
            })
            return
          }
          if (!parenthesesRightRex.test(value.parenthesesRight)) {
            ElMessage({
              message: `第${i + 1}行右括号输入不正确!`,
              type: 'error',
              duration: 1500
            })
            return
          }
          if (i !== (expressionList.value.length - 1) && !value.operator) {
            ElMessage({
              message: `第${i + 1}行运算符不能为空!`,
              type: 'error',
              duration: 1500
            })
            return
          }
          expression = expression + value.parenthesesLeft + value.itemNo + value.parenthesesRight + (i === (expressionList.value.length - 1) ? '' : value.operator)
        }
        formData.value.calItem.expression = expression
      } else {
        ElMessage({
          message: `表达式不可以为空`,
          type: 'error',
          duration: 1500
        })
        return
      }
      formData.value.itemType = 'CAL'
      const data = formData.value as ItemApi.ItemVO
      if (formType.value === 'create') {
        await ItemApi.createItem(data)
        message.success(t('common.createSuccess'))
      } else {
        await ItemApi.updateItem(data)
        message.success(t('common.updateSuccess'))
      }
      dialogVisible.value = false
      // 发送操作成功的事件
      emit('success')
    } finally {
      formLoading.value = false
    }
  }
  function addExpressionRow(index, rows) {
    let row = JSON.parse(JSON.stringify(rows[index]))
    rows.splice(index, 0, row)
  }
  function addParenthesesLeft(index, row) {
    if (row.parenthesesLeft) {
      row.parenthesesLeft = row.parenthesesLeft + '('
    } else {
      row.parenthesesLeft = '('
    }
  }
  function removeParenthesesLeft(index, row) {
    if (row.parenthesesLeft) {
      row.parenthesesLeft = row.parenthesesLeft.substring(0, row.parenthesesLeft.length - 1)
    } else {
      row.parenthesesLeft = ''
    }
  }
  function addParenthesesRight(index, row) {
    if (row.parenthesesRight) {
      row.parenthesesRight = row.parenthesesRight + ')'
    } else {
      row.parenthesesRight = ')'
    }
  }
  function removeParenthesesRight(index, row) {
    if (row.parenthesesRight) {
      row.parenthesesRight = row.parenthesesRight.substring(0, row.parenthesesRight.length - 1)
    } else {
      row.parenthesesRight = ''
    }
  }
  function deleteExpressionRow(index, rows) {
    if (!rows || rows.length === 1) {
      ElMessage({
        message: '不能全部删除!',
        type: 'error',
        duration: 1500
      })
      return
    }
    rows.splice(index, 1)
  }
  /** 重置表单 */
  const resetForm = () => {
    formData.value = {
      id: undefined,
      itemNo: '',
      itemName: '',
      itemType: '',
      itemCategory: '',
      coefficient: '',
      precision: '',
      businessType: '',
      timeRange: '',
      timeGranularity: '',
      remark: '',
      calItem: {
        id: '',
        expression: '',
      }}
    formRef.value?.resetFields()
  }
</script>