潘志宝
2024-09-24 d57817d30107e15f870799953831f63d2f9087ce
Merge remote-tracking branch 'origin/master'
已添加9个文件
已修改3个文件
1724 ■■■■■ 文件已修改
src/api/data/ind/item/item.ts 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/dict.ts 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/ind/img/ATOM.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/ind/img/CAL.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/ind/img/DER.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/ind/item/AtomIndDefineForm.vue 263 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/ind/item/CalIndDefineForm.vue 436 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/ind/item/DerIndDefineForm.vue 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/ind/item/index.vue 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/data/ind/item/selectItemType.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/predict/MmPredictItemForm.vue 324 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/predict/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/data/ind/item/item.ts
对比新文件
@@ -0,0 +1,66 @@
import request from '@/config/axios'
export type ItemVO = {
  id: string | undefined
  itemNo: string
  itemName: string
  itemType: string
  itemCategory: string
  coefficient: number
  precision: number
  timeGranularity: string
  unit: string
  remark: string
  status: string
  timeLabel: string
  timeLimit: string
  timeStart: string
  timeEnd: string
  dimension: string
  expression: string
}
export type PageParam = {
  itemNo: string
  itemName: string
  itemType: string
  itemCategory: string
}
// 查询列表
export const getItemPage = (params: PageParam) => {
  return request.get({ url: '/data/ind-item/page', params })
}
// 查询详情
export const getItem = (id: string) => {
  return request.get({ url: '/data/ind-item/get?id=' + id })
}
// 新增
export const createItem = (data: ItemVO) => {
  return request.post({ url: '/data/ind-item/create', data })
}
// 修改
export const updateItem = (data: ItemVO) => {
  return request.put({ url: '/data/ind-item/update', data })
}
// 删除
export const deleteItem = (id: number) => {
  return request.delete({ url: '/data/ind-item/delete?id=' + id })
}
//获取下拉集合
export const getItemList = (params: PageParam) => {
  return request.get({ url: '/data/ind-item/getList', params})
}
export const validateAsNumber = (rule, value, callback) => {
  const regex = /^(\-|\+)?\d+(\.\d+)?$/;
  if (!regex.test(value)) {
    callback(new Error('请输入数字!'));
  }
}
src/utils/dict.ts
@@ -248,5 +248,8 @@
  OPCUA_SECURITY_MODE = 'opcua_security_mode',
  OPCUA_CONNECTION_TYPE = 'opcua_connection_type',
  HTTP_METHOD = 'http_method',
  TIME_GRANULARITY = 'time_granularity',
  STAT_FUNC = 'stat_func',
  TIME_LIMIT = 'time_limit',
  ITEM_TYPE = 'item_type',
}
src/views/data/ind/img/ATOM.png
src/views/data/ind/img/CAL.png
src/views/data/ind/img/DER.png
src/views/data/ind/item/AtomIndDefineForm.vue
对比新文件
@@ -0,0 +1,263 @@
<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="12">
          <el-form-item label="数据集" prop="atomItem.dataSet">
            <el-select v-model="formData.atomItem.dataSet" clearable placeholder="请选择数据集" @change="handleDataSetChange($event)">
              <el-option
                v-for="item in dataSetList"
                :key="item.id"
                :label="item.name"
                :value="item.id + ''"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="使用字段" prop="atomItem.usingField">
            <el-select v-model="formData.atomItem.usingField" clearable placeholder="请选择字段">
              <el-option
                v-for="item in dataSetFieldList"
                :key="item.id"
                :label="item.fieldCode"
                :value="item.id + ''"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="统计方式" prop="statFunc">
            <el-select v-model="formData.atomItem.statFunc" placeholder="请选择">
              <el-option
                v-for="dict in getStrDictOptions(DICT_TYPE.STAT_FUNC)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
              />
            </el-select>
          </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 ItemApi from '@/api/data/ind/item/item'
  import * as DataSetApi from '@/api/data/ind/data/data.set'
  import * as DataSetFieldApi from '@/api/data/ind/data/data.field'
  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 - 修改
  let formData = ref({
    id: undefined,
    itemNo: '',
    itemName: '',
    itemType: '',
    itemCategory: '',
    coefficient: '',
    precision: '',
    businessType: '',
    timeRange: '',
    timeGranularity: '',
    remark: '',
    atomItem:{
      dataSource:'',
      dataSet: '',
      usingField: '',
      statFunc: ''
    }
  })
  const validateAsNumber = (rule, value, callback) => {
    const regex = /^(\-|\+)?\d+(\.\d+)?$/;
    if (!regex.test(value)) {
      callback(new Error('请输入数字!'));
    }
  }
  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' }],
    "atomItem.dataSet": [{required: true, message: '数据集不能为空', trigger: 'blur'}],
    "atomItem.usingField":[{required: true, message: '使用字段不能为空', trigger: 'blur'}]
  })
  const formRef = ref() // 表单 Ref
  const dataSetList = ref([] as DataSetApi.DataSetVO[])
  const dataSetFieldList = ref([] as DataSetFieldApi.DataSetFieldVO[])
  const dataCategoryList = ref([])
  /** 打开弹窗 */
  const open = async (type: string, id?: string) => {
    dialogVisible.value = true
    dialogTitle.value = '原子指标'
    formType.value = type
    resetForm()
    // 加载数据源列表
    dataSetList.value = await DataSetApi.getDataSetList()
    dataCategoryList.value = await CategoryApi.getCategoryListAllSimple()
    // 修改时,设置数据
    if (id) {
      formLoading.value = true
      try {
        formData.value = await ItemApi.getItem(id)
        if(formData.value.atomItem.dataSet !== null){
          const queryParams = reactive({
            dataSetId: formData.value.atomItem.dataSet,
          })
          dataSetFieldList.value = (await DataSetFieldApi.getDataSetFieldPage(queryParams)).list
        }
      } finally {
        formLoading.value = false
      }
    }
  }
  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 {
      formData.value.itemType = 'ATOM'
      for (let index in dataSetList.value){
        if(formData.value.atomItem.dataSet === dataSetList.value[index].id){
          formData.value.atomItem.dataSource = dataSetList.value[index].dataSource
        }
      }
      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
    }
  }
  /** 重置表单 */
  const resetForm = () => {
    formData.value = {
      id: '',
      itemNo: '',
      itemName: '',
      itemType: '',
      itemCategory: '',
      coefficient: '',
      precision: '',
      businessType: '',
      timeRange: '',
      timeGranularity: '',
      remark: '',
      atomItem:{
        dataSource:'',
        dataSet: '',
        usingField: '',
        statFunc: ''
      }
    }
    dataSetFieldList.value = []
    formRef.value?.resetFields()
  }
  async function handleDataSetChange(event) {
    if (event !== null && event !== undefined) {
      const queryParams = reactive({
        dataSetId: event,
      })
      dataSetFieldList.value = (await DataSetFieldApi.getDataSetFieldPage(queryParams)).list
    }
  }
</script>
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>
src/views/data/ind/item/DerIndDefineForm.vue
对比新文件
@@ -0,0 +1,329 @@
<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="atomItem.id">
            <el-select v-model="selected" clearable placeholder="请选择原子指标"
                       @change="handleChange($event)">
              <el-option
                v-for="item in atomItemList"
                :key="item.id"
                :label="item.itemNo"
                :value="item.id"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="原子指标名称" prop="atomItem.itemName">
            <el-input v-model="formData.atomItem.itemName" disabled/>
          </el-form-item>
        </el-col>
      </el-row>
      <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"/>
          </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="6">
          <el-form-item label="数量单位" prop="unit">
            <el-input v-model="formData.unit"/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="20">
          <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="12">
          <el-form-item label="时间标识" prop="timeLabel">
            <el-select v-model="formData.derItem.timeLabel" clearable placeholder="请选择时间标识">
              <el-option
                v-for="item in dataSetFieldList"
                :key="item.id"
                :label="item.fieldCode"
                :value="item.id + ''"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="时间限定" prop="timeLimit">
            <el-select v-model="formData.timeLimit" placeholder="请选择"
                       @change="handleTimeLimitChange($event)">
              <el-option
                v-for="dict in getStrDictOptions(DICT_TYPE.TIME_LIMIT)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
              />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row v-if="showTimeChange">
        <el-col :span="12">
          <el-form-item label="开始时间" prop="timeStart">
            <el-date-picker
              v-model="formData.derItem.timeStart"
              type="datetime"
              placeholder="请选择开始时间"
            />
          </el-form-item>
        </el-col>
        <el-col :span="8">
          <el-form-item label="结束时间" prop="timeEnd">
            <el-date-picker
              v-model="formData.derItem.timeEnd"
              type="datetime"
              placeholder="请选择结束时间"
            />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="分析维度" prop="dimension">
            <el-select v-model="formData.derItem.dimension" clearable placeholder="请选择分析维度" multiple>
              <el-option
                v-for="item in dataSetFieldList"
                :key="item.id"
                :label="item.fieldCode"
                :value="item.id + ''"
              />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <template #footer>
      <el-button :="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 * as ItemApi from '@/api/data/ind/item/item'
  import {CommonStatusEnum} from '@/utils/constants'
  import * as DataSourceConfigApi from "@/api/infra/dataSourceConfig";
  import {PageParam} from "@/api/data/ind/item/item";
  import * as CategoryApi from "@/api/data/ind/category";
  import * as DataSetFieldApi from "@/api/data/ind/data/data.field";
  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 - 修改
  let formData = ref({
    id: undefined,
    itemNo: '',
    itemName: '',
    itemType: '',
    itemCategory: '',
    coefficient: '',
    precision: '',
    businessType: '',
    timeRange: '',
    timeGranularity: '',
    atomItem: {
      id: '',
      itemNo: '',
      itemName: '',
    },
    derItem: {
      atomItemId: '',
      timeLabel: '',
      timeLimit: '',
      timeStart: '',
      timeEnd: '',
      dimension: ''
    }
  })
  const queryParams = ref({
    itemType: 'ATOM'
  })
  const validateAsNumber = (rule, value, callback) => {
    const regex = /^(\-|\+)?\d+(\.\d+)?$/;
    if (!regex.test(value)) {
      callback(new Error('请输入数字!'));
    }
  }
  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' }],
    "atomItem.id": [{required: true, message: '原子指标不能为空', trigger: 'blur'}]
  })
  const formRef = ref() // 表单 Ref
  const dataSourceList = ref([] as DataSourceConfigApi.DataSourceConfigVO[])
  const atomItemList = ref([] as ItemApi.ItemVO[])
  const selected = ref(null)
  const showTimeChange = ref(false)
  const dataCategoryList = ref([] as CategoryApi.IndItemCategoryVO[])
  const dataSetFieldList = ref([] as DataSetFieldApi.DataSetFieldVO[])
  /** 打开弹窗 */
  const open = async (type: string, id?: string) => {
    dialogVisible.value = true
    dialogTitle.value = '派生指标'
    formType.value = type
    resetForm()
    // 加载数据源列表
    dataCategoryList.value = await CategoryApi.getCategoryListAllSimple()
    atomItemList.value = await ItemApi.getItemList(queryParams)
    selected.value = null
    // 修改时,设置数据
    if (id) {
      formLoading.value = true
      try {
        formData.value = await ItemApi.getItem(id)
      } finally {
        formLoading.value = false
      }
    }
  }
  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 {
      formData.value.itemType = 'DER'
      formData.value.derItem.atomItemId = formData.value.atomItem.id
      if(formData.value.derItem.dimension.length > 0){
        let dimension = ''
        for (let index in formData.value.derItem.dimension){
          dimension = dimension + formData.value.derItem.dimension[index] + ','
        }
        formData.value.derItem.dimension = dimension.substring(0, dimension.length - 1)
      }
      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
    }
  }
  /** 重置表单 */
  const resetForm = () => {
    formData.value = {
      id: undefined,
      itemNo: '',
      itemName: '',
      itemType: '',
      itemCategory: '',
      coefficient: '',
      precision: '',
      businessType: '',
      timeRange: '',
      timeGranularity: '',
      remark: '',
      atomItem: {
        id: '',
        itemNo: '',
        itemName: '',
      },
      derItem: {
        atomItemId: '',
        timeLabel: '',
        timeLimit: '',
        timeStart: '',
        timeEnd: '',
        dimension: ''
      }
    }
  }
  async function handleChange(event) {
    if (event !== null && event !== undefined) {
      const itemData = await ItemApi.getItem(event)
      formData.value.atomItem.itemName = itemData.itemName
      formData.value.atomItem.id = itemData.atomItem.id
      const queryParams = reactive({
        dataSetId: itemData.atomItem.dataSet,
      })
      dataSetFieldList.value = (await DataSetFieldApi.getDataSetFieldPage(queryParams)).list
    }
  }
  function handleTimeLimitChange(event) {
    showTimeChange.value = event === 'CUSTOM';
  }
</script>
src/views/data/ind/item/index.vue
对比新文件
@@ -0,0 +1,195 @@
<template>
  <!-- 搜索工作栏 -->
  <ContentWrap>
    <el-form ref="queryFormRef" :inline="true" :model="queryParams" class="-mb-15px"
             label-width="68px">
      <el-form-item label="指标编码" prop="name">
        <el-input v-model="queryParams.itemNo" class="!w-240px" clearable placeholder="请输入指标编码"
                  @keyup.enter="handleQuery"/>
      </el-form-item>
      <el-form-item label="指标名称" prop="name">
        <el-input v-model="queryParams.itemName" class="!w-240px" clearable placeholder="请输入指标名称"
                  @keyup.enter="handleQuery"/>
      </el-form-item>
      <el-form-item>
        <el-button @click="handleQuery">
          <Icon class="mr-5px" icon="ep:search"/>
          搜索
        </el-button>
        <el-button @click="resetQuery">
          <Icon class="mr-5px" icon="ep:refresh"/>
          重置
        </el-button>
        <el-button
          v-hasPermi="['data:ind-item:create']"
          plain
          type="primary"
          @click="openForm('create')"
        >
          <Icon class="mr-5px" icon="ep:plus"/>
          新增
        </el-button>
      </el-form-item>
    </el-form>
  </ContentWrap>
  <!-- 列表 -->
  <ContentWrap>
    <el-table v-loading="loading" :data="list">
      <el-table-column prop="itemNo" label="指标编码" header-align="center" align="center" min-width="80"/>
      <el-table-column prop="itemName" label="指标名称" header-align="center" align="center" min-width="120"/>
      <el-table-column prop="itemCategoryName" label="指标分类" header-align="center" align="center" min-width="100"/>
      <el-table-column prop="itemType" label="指标类型" header-align="center" align="center" min-width="60">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.ITEM_TYPE" :value="scope.row.itemType" />
        </template>
      </el-table-column>
      <el-table-column prop="coefficient" label="系数" header-align="center" align="center" min-width="60"/>
      <el-table-column prop="precision" label="指标精度" header-align="center" align="center" min-width="60"/>
      <el-table-column prop="timeGranularity" label="时间粒度" header-align="center" align="center" min-width="40">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.TIME_GRANULARITY" :value="scope.row.timeGranularity" />
        </template>
      </el-table-column>
      <el-table-column
        :formatter="dateFormatter"
        align="center"
        label="创建时间"
        prop="createTime"
        width="180"/>
      <el-table-column align="center" label="操作">
        <template #default="scope">
          <el-button
            v-hasPermi="['data:ind-item:update']"
            link
            type="primary"
            @click="openForm('update', scope.row)">
            修改
          </el-button>
          <el-button
            v-hasPermi="['data:ind-item:delete']"
            link
            type="danger"
            @click="handleDelete(scope.row.id)">
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <Pagination
      v-model:limit="queryParams.pageSize"
      v-model:page="queryParams.pageNo"
      :total="total"
      @pagination="getList"
    />
  </ContentWrap>
  <!-- 表单弹窗:添加/修改 -->
  <AtomIndDefineForm ref="atomFormRef" @success="getList" />
  <DerIndDefineForm ref="derFormRef" @success="getList" />
  <CalIndDefineForm ref="calFormRef" @success="getList" />
  <SelectItemType ref="itemTypeSel"/>
</template>
<script lang="ts" setup>
  import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
  import { dateFormatter } from '@/utils/formatTime'
  import * as DataSetApi from '@/api/data/ind/data/data.set'
  import AtomIndDefineForm from './AtomIndDefineForm.vue'
  import DerIndDefineForm from './DerIndDefineForm.vue'
  import CalIndDefineForm from './CalIndDefineForm.vue'
  import SelectItemType from './selectItemType.vue'
  import download from '@/utils/download'
  import * as ItemApi from '@/api/data/ind/item/item'
  import * as CategoryApi from "@/api/data/ind/category";
  defineOptions({ name: 'IndDataSet' })
  const message = useMessage() // 消息弹窗
  const { t } = useI18n() // 国际化
  const dataCategoryList = ref([] as CategoryApi.IndItemCategoryVO[])
  const loading = ref(true) // 列表的加载中
  const total = ref(0) // 列表的总页数
  const list = ref([]) // 字典表格数据
  const queryParams = reactive({
    pageNo: 1,
    pageSize: 10,
    itemNo: '',
    itemName: '',
    itemType: '',
    itemCategory: ''
  })
  const queryFormRef = ref() // 搜索的表单
  const exportLoading = ref(false) // 导出的加载中
  /** 查询字典类型列表 */
  const getList = async () => {
    loading.value = true
    try {
      const data = await ItemApi.getItemPage(queryParams)
      list.value = data.list
      total.value = data.total
      dataCategoryList.value = await CategoryApi.getCategoryListAllSimple()
    } finally {
      loading.value = false
    }
  }
  /** 搜索按钮操作 */
  const handleQuery = () => {
    queryParams.pageNo = 1
    getList()
  }
  /** 重置按钮操作 */
  const resetQuery = () => {
    queryFormRef.value.resetFields()
    handleQuery()
  }
  /** 添加/修改操作 */
  const atomFormRef = ref()
  const derFormRef = ref()
  const calFormRef = ref()
  const itemTypeSel = ref()
  const openForm = (type: string, row) => {
    if(row !== null && row !==undefined){
      if(row.itemType === 'ATOM'){
        if (row.id){
          atomFormRef.value.open(type,row.id)
        }
      }else if(row.itemType === 'CAL'){
        if (row.id){
          calFormRef.value.open(type,row.id)
        }
      }else if(row.itemType === 'DER'){
        if (row.id){
          derFormRef.value.open(type,row.id)
        }
      }
    }else {
      itemTypeSel.value.open(type)
    }
  }
  /** 删除按钮操作 */
  const handleDelete = async (id: number) => {
    try {
      // 删除的二次确认
      await message.delConfirm()
      // 发起删除
      await ItemApi.deleteItem(id)
      message.success(t('common.delSuccess'))
      // 刷新列表
      await getList()
    } catch {}
  }
  /** 初始化 **/
  onMounted(() => {
    getList()
  })
</script>
src/views/data/ind/item/selectItemType.vue
对比新文件
@@ -0,0 +1,96 @@
<template>
  <Dialog v-model="dialogVisible" :title="dialogTitle" width="47%">
    <div class="card" @click="openForm('ATOM')">
      <img src="../img/ATOM.png" alt="Card Image" class="card-img"/>
      <div class="card-body">
        <p class="card-title-text">{{ "原子指标" }}</p>
        <p class="card-text">{{ "从0开始创建一个新指标" }}</p>
      </div>
    </div>
    <div class="card" @click="openForm('DER')">
      <img src="../img/DER.png" alt="Card Image" class="card-img"/>
      <div class="card-body">
        <p class="card-title-text">{{ "派生指标" }}</p>
        <p class="card-text">{{ "基于单个原子指标通过条件过滤派生出的新指标" }}</p>
      </div>
    </div>
    <div class="card" @click="openForm('CAL')">
      <img src="../img/CAL.png" alt="Card Image" class="card-img"/>
      <div class="card-body">
        <p class="card-title-text">{{ "复合指标" }}</p>
        <p class="card-text">{{ "基于一组原子指标通过条件过滤派生出的新指标" }}</p>
      </div>
    </div>
  </Dialog>
  <AtomIndDefineForm ref="atomFormRef" @success="getList" />
  <DerIndDefineForm ref="derFormRef" @success="getList" />
  <CalIndDefineForm ref="calFormRef" @success="getList" />
</template>
<script lang="ts" setup>
  import * as DataSetApi from '@/api/data/ind/data/data.set'
  import AtomIndDefineForm from './AtomIndDefineForm.vue'
  import DerIndDefineForm from './DerIndDefineForm.vue'
  import CalIndDefineForm from './CalIndDefineForm.vue'
  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 open = async (type: string) => {
    dialogVisible.value = true
    dialogTitle.value = t('action.' + type)
    formType.value = type
  }
  defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  const atomFormRef = ref()
  const derFormRef = ref()
  const calFormRef = ref()
  const openForm = (category: string) => {
    if(category === 'ATOM'){
      atomFormRef.value.open("create",null)
    }else if(category === 'DER'){
      derFormRef.value.open("create",null)
    }else if(category === 'CAL'){
      calFormRef.value.open("create",null)
    }
  }
</script>
<style scoped>
  .card {
    width: 25vh;
    margin: 10px 16px;
    display: inline-block;
    vertical-align: top;
    border: 1px solid #ddd;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    height: 30vh;
    cursor: pointer;
  }
  .card-img {
    width: 100%;
    height: 150px;
    object-fit: cover;
  }
  .card-body {
    padding: 10px;
    text-align: center;
  }
  .card-title-text{
    font-size: larger;
  }
  .card-text {
    font-size: smaller;
    margin: 0;
  }
</style>
src/views/model/pre/predict/MmPredictItemForm.vue
@@ -10,24 +10,20 @@
      <el-divider content-position="left">基本信息</el-divider>
      <el-row>
        <el-col :span="12">
          <el-form-item label="预测项名" prop="mmPredictItem.itemname">
            <el-input v-model="dataForm.mmPredictItem.itemname" placeholder="预测项名"
                      maxlength="50"
                      clearable/>
          <el-form-item label="预测项名" prop="itemname">
            <el-input v-model="dataForm.mmPredictItem.itemname" placeholder="预测项名"/>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="编号" prop="mmPredictItem.itemno">
            <el-input v-model="dataForm.mmPredictItem.itemno" placeholder="编号" maxlength="50"
                      clearable/>
          <el-form-item label="编号" prop="itemno">
            <el-input v-model="dataForm.mmPredictItem.itemno" placeholder="编号" maxlength="50"/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="类型" prop="mmPredictItem.itemtypeid">
            <el-select v-model="dataForm.mmPredictItem.itemtypeid"
                       @change="changeItemtype"
          <el-form-item label="类型" prop="itemtypeid">
            <el-select v-model="dataForm.mmPredictItem.itemtypeid" @change="changeItemtype"
                       placeholder="请选择">
              <el-option
                v-for="item in itemTypeList"
@@ -38,15 +34,15 @@
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="粒度" prop="mmPredictItem.granularity">
            <el-input v-model="dataForm.mmPredictItem.granularity" placeholder="粒度" maxlength="5"
                      clearable/>
          <el-form-item label="粒度" prop="granularity">
            <el-input v-model="dataForm.mmPredictItem.granularity" placeholder="粒度"
                      maxlength="5"/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="是否检查" prop="mmPredictItem.workchecked">
          <el-form-item label="是否检查" prop="workchecked">
            <el-select v-model="dataForm.mmPredictItem.workchecked" placeholder="请选择">
              <el-option
                v-for="item in isList"
@@ -57,7 +53,7 @@
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="是否启用" prop="dmModuleItem.status">
          <el-form-item label="是否启用" prop="status">
            <el-select v-model="dataForm.dmModuleItem.status" placeholder="请选择">
              <el-option
                v-for="item in isList"
@@ -70,7 +66,7 @@
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="管网" prop="dmModuleItem.moduleid">
          <el-form-item label="管网" prop="moduleid">
            <el-select v-model="dataForm.dmModuleItem.moduleid" placeholder="请选择">
              <el-option
                v-for="item in moduleList"
@@ -81,21 +77,19 @@
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="类别" prop="dmModuleItem.categoryid">
            <el-input v-model="dataForm.dmModuleItem.categoryid" placeholder="类别" maxlength="64"
                      clearable/>
          <el-form-item label="类别" prop="categoryid">
            <el-input v-model="dataForm.dmModuleItem.categoryid" placeholder="类别" maxlength="64"/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="排序" prop="dmModuleItem.itemorder">
            <el-input v-model="dataForm.dmModuleItem.itemorder" placeholder="排序" maxlength="36"
                      clearable/>
          <el-form-item label="排序" prop="itemorder">
            <el-input v-model="dataForm.dmModuleItem.itemorder" placeholder="排序" maxlength="36"/>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="数据点" prop="mmItemOutput.pointid">
          <el-form-item label="数据点" prop="pointid">
            <el-select
              v-model="dataForm.mmItemOutput.pointid"
              filterable
@@ -112,7 +106,7 @@
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="保存点位" prop="mmPredictItem.saveindex">
          <el-form-item label="保存点位" prop="saveindex">
            <el-select v-model="dataForm.mmPredictItem.saveindex" placeholder="请选择">
              <el-option
                v-for="item in saveIndexList"
@@ -125,10 +119,9 @@
      </el-row>
      <el-row v-if="dataForm.itemtypename === 'MergeItem'">
        <el-col :span="12">
          <el-form-item label="预测长度" prop="mmPredictItem.predictlength">
          <el-form-item label="预测长度" prop="predictlength">
            <el-input v-model="dataForm.mmPredictItem.predictlength" placeholder="预测长度"
                      maxlength="5"
                      clearable/>
                      maxlength="5"/>
          </el-form-item>
        </el-col>
      </el-row>
@@ -136,16 +129,14 @@
      </el-divider>
      <el-row v-if="dataForm.itemtypename === 'NormalItem'">
        <el-col :span="24">
          <el-form-item label="模型名称" prop="mmPredictModel.modelname">
          <el-form-item label="模型名称" prop="modelname">
            <el-input v-model="dataForm.mmPredictModel.modelname" placeholder="模型名称"
                      maxlength="50"
                      clearable/>
                      maxlength="50"/>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="编号" prop="mmPredictModel.modelno" v-if="!!dataForm.id">
          <el-form-item label="编号" prop="modelno" v-if="!!dataForm.id">
            <el-input v-model="dataForm.mmPredictModel.modelno" placeholder="编号" maxlength="32"
                      clearable
                      disabled/>
          </el-form-item>
        </el-col>
@@ -170,7 +161,7 @@
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="结果" prop="mmPredictModel.resultstrid">
          <el-form-item label="结果" prop="resultstrid">
            <el-select v-model="dataForm.mmPredictModel.resultstrid" placeholder="请选择">
              <el-option
                v-for="item in resultstridList"
@@ -183,35 +174,31 @@
      </el-row>
      <el-row v-if="dataForm.itemtypename === 'NormalItem'">
        <el-col :span="24">
          <el-form-item label="路径" prop="mmPredictModel.modelpath">
          <el-form-item label="路径" prop="modelpath">
            <el-input v-model="dataForm.mmPredictModel.modelpath" placeholder="路径" maxlength="32"
                      clearable
                      disabled/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row v-if="dataForm.itemtypename === 'NormalItem'">
        <el-col :span="24">
          <el-form-item label="类名" prop="mmPredictModel.classname">
          <el-form-item label="类名" prop="classname">
            <el-input v-model="dataForm.mmPredictModel.classname" placeholder="类名" maxlength="32"
                      clearable
                      disabled/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row v-if="dataForm.itemtypename === 'NormalItem'">
        <el-col :span="12">
          <el-form-item label="方法名" prop="mmPredictModel.methodname">
          <el-form-item label="方法名" prop="methodname">
            <el-input v-model="dataForm.mmPredictModel.methodname" placeholder="方法名"
                      maxlength="32" clearable
                      disabled/>
                      maxlength="32" disabled/>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="参数" prop="mmPredictModel.modelparamstructure">
          <el-form-item label="参数" prop="modelparamstructure">
            <el-input v-model="dataForm.mmPredictModel.modelparamstructure" placeholder="参数"
                      maxlength="32" clearable
                      disabled/>
                      maxlength="32" disabled/>
          </el-form-item>
        </el-col>
      </el-row>
@@ -226,8 +213,7 @@
        <el-table-column prop="valuetype" label="类型" align="center"/>
        <el-table-column prop="" label="值" align="center">
          <template #default="scope">
            <el-input v-model="scope.row.value" maxlength="256" clearable
                      style="width:100%;hight:100%"/>
            <el-input v-model="scope.row.value" maxlength="256" style="width:100%;height:100%"/>
          </template>
        </el-table-column>
      </el-table>
@@ -269,7 +255,7 @@
        </el-table-column>
        <el-table-column prop="" label="参数长度" width="120" align="center">
          <template #default="scope">
            <el-input v-model="scope.row.datalength" maxlength="50" clearable
            <el-input v-model="scope.row.datalength" maxlength="50"
                      style="width:100%;hight:100%"/>
          </template>
        </el-table-column>
@@ -365,7 +351,6 @@
import * as DmModule from '@/api/model/pre/dm'
import * as MmResultTable from '@/api/model/pre/result'
import * as DaPoint from '@/api/data/da/point'
import request from "@/config/axios";
import * as ScheduleModelApi from '@/api/model/sche/model'
defineOptions({name: 'DataMmPredictItemForm'})
@@ -497,11 +482,18 @@
  dialogTitle.value = t('action.' + type)
  formType.value = type
  resetForm()
  setDefaultFields()
  // getItemTypeList()
  // getModuleList()
  getPointList()
  // getModelparamList()
  // getResulttableList()
  // getResultstridList()
  // 修改时,设置数据
  if (id) {
    formLoading.value = true
    try {
      dataForm.value = await MmPredictItem.getMmPredictItem(id)
      getInfo(id)
    } finally {
      formLoading.value = false
    }
@@ -534,10 +526,40 @@
    formLoading.value = false
  }
}
const setReplaceModelOnly = (value) => {
function getInfo(id) {
  debugger
  const res = MmPredictItem.getMmPredictItem(id)
  console.info(res)
  expressionList.value = []
  if (res.mmPredictMergeItem && res.mmPredictMergeItem.expression) {
    let expression = res.mmPredictMergeItem.expression
    do {
      let indexPlus = expression.indexOf('+')
      let indexSub = expression.indexOf('-')
      if (indexPlus !== -1 || indexSub !== -1) {
        let endIndex = (indexSub == -1 || (indexPlus < indexSub && indexPlus !== -1)) ? indexPlus : indexSub
        expressionList.value.push({
          point: expression.substring(0, endIndex),
          operator: expression.substr(endIndex, 1)
        })
        expression = expression.substring(endIndex + 1)
      } else {
        expressionList.value.push({
          point: expression,
          operator: ''
        })
        expression = ''
      }
    } while (expression && expression.length > 0)
  }
}
function setReplaceModelOnly(value) {
  replaceModelOnly.value = value
}
const beforeUpload = (file) => {
function beforeUpload(file) {
  let fileName = file.name
  let first = fileName.lastIndexOf('.')
  let nameLength = fileName.length
@@ -547,7 +569,8 @@
    return
  }
}
const uploadModelSuccess = (response, file, fileList) => {
function uploadModelSuccess(response, file, fileList) {
  if (response.code === 0) {
    message.success(t('上传成功'))
    dataForm.value.mmModelArithSettingsList = []
@@ -557,7 +580,7 @@
    dataForm.value.mmPredictModel.modelpath = ''
    dataForm.value.mmPredictModel.modelparamstructure = ''
    if (response.data.loadFieldSetList && response.data.loadFieldSetList[0].propertyList) {
      response.data.loadFieldSetList[0].propertyList.forEach((value) => {
      response.data.loadFieldSetList[0].propertyList.forEach(function (value) {
        if (value.key !== 'data1') {
          dataForm.value.mmModelArithSettingsList.push({
            key: value.key,
@@ -597,28 +620,36 @@
  }
  fileList = []
}
const uploadModelError = (file, err, fileList) => {
function uploadModelError(file, err, fileList) {
}
const changeModelparam = (row) => {
function changeModelparam(row) {
  row.modelparamname = modelparamMap[row.modelparamid]
}
const changeItemtype = (value) => {
function changeItemtype(value) {
  dataForm.value.itemtypename = itemTypeMap[value]
}
const changeModelparamtype = (value, row) => {
function changeModelparamtype(value, row) {
  row.modelparamid = ''
}
const changeOutputPoint = (value) => {
function changeOutputPoint(value) {
  dataForm.value.mmItemOutput.tagname = pointMap[value]
}
const deleteExpressionRow = (index, rows) => {
function deleteExpressionRow(index, rows) {
  rows.splice(index, 1)
}
const addExpressionRow = (index, rows) => {
function addExpressionRow(index, rows) {
  let row = JSON.parse(JSON.stringify(rows[index]))
  rows.splice(index, 0, row)
}
const deleteRow = (index: string, rows) => {
function deleteRow(index: string, rows) {
  if (!rows || rows.length === 1) {
    message.error('不能全部删除!')
    return
@@ -626,13 +657,14 @@
  rows.splice(index, 1)
  orderRow(rows)
}
const addRow = (index: string, rows) => {
function addRow(index: string, rows) {
  let row = JSON.parse(JSON.stringify(rows[index]))
  rows.splice(index, 0, row)
  orderRow(rows)
}
const orderRow = (rows) => {
function orderRow(rows) {
  let modelparamorder = 0
  let modelparamportorder = 0
  rows.forEach(function (value) {
@@ -644,108 +676,145 @@
    modelparamorder++
  })
}
// 获取预测项类型列表
const getItemTypeList = async () => {
function getItemTypeList() {
  itemTypeList.value = []
  itemTypeList.value = await MmItemType.getItemTypeList()
  itemTypeList.value.forEach((value) => {
  itemTypeList.value = MmItemType.getItemTypeList()
  itemTypeList.value.forEach(function (value) {
    itemTypeMap[value.id] = value.itemtypename
  })
  if (!dataForm.value.id) {
    // dataForm.value.mmPredictItem.itemtypeid = itemTypeList[0].id
  }
}
// 获取管网列表
const getModuleList = async () => {
function getModuleList() {
  moduleList.value = []
  moduleList.value = await DmModule.getModuleList()
  moduleList.value = DmModule.getModuleList()
}
const getResulttableList = async () => {
function getResulttableList() {
  resulttableList.value = []
  resulttableList.value = await MmResultTable.getResulttableList()
  resulttableList.value = MmResultTable.getResulttableList()
}
const getResultstridList = async () => {
function getResultstridList() {
  resultstridList.value = []
  resultstridList.value = await MmResultTable.getResultstridList()
  resultstridList.value = MmResultTable.getResultstridList()
}
const getPointList = async () => {
function getPointList() {
  pointLoading.value = true
  pointList.value = await DaPoint.getPointList(queryParams)
  pointList.value.forEach((value) => {
    pointList.value.push(value)
    pointMap[value.id] = value.pointname
  })
  pointList.value = DaPoint.getPointList(queryParams)
  if (pointList.value.length > 0) {
    pointList.value.forEach(function (value) {
      pointList.value.push(value)
      pointMap[value.id] = value.pointname
    })
  }
}
const getModelparamList = async () => {
function getModelparamList() {
  modelparamListMap.value = []
  modelparamList.value = []
  predictItemList.value = []
  let pointRes = await DaPoint.getPointList(queryParams)
  pointList.value = DaPoint.getPointList(queryParams)
  let paramList = []
  pointRes.forEach((value) => {
    paramList.push({
      id: value.id,
      code: value.pointno,
      name: value.pointname,
      type: 'DATAPOINT'
  if (pointList.value.length > 0) {
    pointList.value.forEach(function (value) {
      paramList.push({
        id: value.id,
        code: value.pointno,
        name: value.pointname,
        type: 'DATAPOINT'
      })
      modelparamMap[value.id] = value.pointname
    })
    modelparamMap[value.id] = value.pointname
  })
  modelparamListMap['DATAPOINT'] = paramList;
    modelparamListMap['DATAPOINT'] = paramList;
  }
  let predictRes = await MmPredictItem.getMmPredictItemList
  let predictRes = MmPredictItem.getMmPredictItemList
  paramList = []
  predictRes.forEach((value) => {
    paramList.push({
      id: value.id,
      code: value.itemno,
      name: value.itemname,
      type: 'PREDICTITEM'
    })
    if (value.id !== dataForm.value.id) {
      predictItemList.push({
  if (predictRes.value.length > 0) {
    predictRes.forEach(function (value) {
      paramList.push({
        id: value.id,
        code: value.itemno,
        name: value.itemname
        name: value.itemname,
        type: 'PREDICTITEM'
      })
    }
    modelparamMap[value.id] = value.itemname
  })
  modelparamListMap['PREDICTITEM'] = paramList;
      if (value.id !== dataForm.value.id) {
        predictItemList.push({
          id: value.id,
          code: value.itemno,
          name: value.itemname
        })
      }
      modelparamMap[value.id] = value.itemname
    })
    modelparamListMap['PREDICTITEM'] = paramList;
  }
  const dayParams = {
    'processType': '日计划'
  }
  let dayScheduleRes = await ScheduleModelApi.getScheduleWorkPrecessList(dayParams)
  let dayScheduleRes = ScheduleModelApi.getScheduleWorkPrecessList(dayParams)
  paramList = []
  dayScheduleRes.forEach((value) => {
    paramList.push({
      id: value.id,
      code: value.code,
      name: value.aliasName,
      type: 'DAYWORKPROCESSPLAN'
  if (dayScheduleRes !== null) {
    dayScheduleRes.forEach(function (value) {
      paramList.push({
        id: value.id,
        code: value.code,
        name: value.aliasName,
        type: 'DAYWORKPROCESSPLAN'
      })
      modelparamMap[value.id] = value.aliasName
    })
    modelparamMap[value.id] = value.aliasName
  })
  modelparamListMap['DAYWORKPROCESSPLAN'] = paramList;
    modelparamListMap['DAYWORKPROCESSPLAN'] = paramList;
  }
  const monthParams = {
    'processType': '月计划'
  }
  let monthScheduleRes = await ScheduleModelApi.getScheduleWorkPrecessList(monthParams)
  let monthScheduleRes = ScheduleModelApi.getScheduleWorkPrecessList(monthParams)
  paramList = []
  monthScheduleRes.forEach((value) => {
    paramList.push({
      id: value.id,
      code: value.code,
      name: value.aliasName,
      type: 'MONTHWORKPROCESSPLAN'
  if (monthScheduleRes !== null) {
    monthScheduleRes.forEach(function (value) {
      paramList.push({
        id: value.id,
        code: value.code,
        name: value.aliasName,
        type: 'MONTHWORKPROCESSPLAN'
      })
      modelparamMap[value.id] = value.aliasName
    })
    modelparamMap[value.id] = value.aliasName
  })
  modelparamListMap['MONTHWORKPROCESSPLAN'] = paramList;
    modelparamListMap['MONTHWORKPROCESSPLAN'] = paramList;
  }
}
function setDefaultFields() {
  dataForm.value.mmPredictItem.workchecked = 0
  dataForm.value.mmPredictItem.predictlength = 60
  dataForm.value.mmPredictItem.status = 1
  dataForm.value.mmPredictItem.isfuse = 0
  dataForm.value.mmPredictItem.predictphase = 0
  dataForm.value.mmPredictItem.unittransfactor = 1
  dataForm.value.mmPredictItem.saveindex = '2'
  dataForm.value.dmModuleItem.status = 1
  dataForm.value.mmPredictModel.trainsamplength = 60
  dataForm.value.mmPredictModel.isonlinetrain = 0
  dataForm.value.mmPredictModel.status = 1
  dataForm.value.mmItemOutput.outputorder = 1
  dataForm.value.mmItemOutput.resulttableid = '3cc2b483-3a01-40f7-a419-0c260210d8eb'
  expressionList.value = [{
    point: '',
    operator: ''
  }]
  fileList.value = []
}
/** 重置表单 */
const resetForm = () => {
  dataForm.value = {
@@ -819,12 +888,7 @@
  init()
})
const init = async () => {
  getItemTypeList()
  getModuleList()
  getPointList()
  getModelparamList()
  getResulttableList()
  getResultstridList()
function init() {
}
</script>
src/views/model/pre/predict/index.vue
@@ -53,18 +53,18 @@
    <el-table v-loading="loading" :data="list">
      <el-table-column label="编号" align="center" prop="itemno" />
      <el-table-column label="预测项名" align="center" prop="itemname" />
      <el-table-column label="类型ID" align="center" prop="itemtypeid" />
<!--      <el-table-column label="类型ID" align="center" prop="itemtypeid" />-->
      <el-table-column label="类型名称" align="center" prop="itemtypename" />
      <el-table-column label="粒度" align="center" prop="granularity" />
      <el-table-column label="是否融合" align="center" prop="isfuse" />
      <el-table-column label="是否检查" align="center" prop="workchecked" />
      <el-table-column label="模块ID" align="center" prop="moduleid" />
<!--      <el-table-column label="模块ID" align="center" prop="moduleid" />-->
      <el-table-column label="排序" align="center" prop="itemorder" />
      <el-table-column label="是否启用" align="center" prop="status" />
      <el-table-column label="类别ID" align="center" prop="categoryid" />
      <el-table-column label="数据点ID" align="center" prop="pointid" />
<!--      <el-table-column label="类别ID" align="center" prop="categoryid" />-->
<!--      <el-table-column label="数据点ID" align="center" prop="pointid" />-->
      <el-table-column label="数据点名称" align="center" prop="tagname" />
      <el-table-column label="存放表ID" align="center" prop="resulttableid" />
<!--      <el-table-column label="存放表ID" align="center" prop="resulttableid" />-->
      <el-table-column label="存放表" align="center" prop="tablename" />
      <el-table-column label="操作" align="center" min-width="110" fixed="right">