liriming
2025-03-03 8bb7160c9c4fd7ce5893ee673647b13cc35410ae
提交 | 用户 | 时间
67ea85 1 <template>
J 2   <Dialog v-model="dialogVisible" :title="dialogTitle" width="55%">
3     <el-form
4       ref="formRef"
5       v-loading="formLoading"
6       :model="formData"
7       :rules="formRules" label-width="100px">
8       <el-row>
9         <el-col :span="12">
10           <el-form-item label="指标编码" prop="itemNo">
11             <el-input v-model="formData.itemNo" disabled/>
12           </el-form-item>
13         </el-col>
14         <el-col :span="12">
15           <el-form-item label="指标名称" prop="itemName">
16             <el-input v-model="formData.itemName" placeholder="请输入指标名称"/>
17           </el-form-item>
18         </el-col>
19       </el-row>
20       <el-row>
21         <el-col :span="12">
22           <el-form-item label="指标分类" prop="itemCategory">
6a72da 23             <el-tree-select
J 24               v-model="formData.itemCategory"
25               :data="dataCategoryList"
26               :default-expanded-keys="[0]"
27               :props="defaultProps"
28               check-strictly
29               node-key="id"
30             />
67ea85 31           </el-form-item>
J 32         </el-col>
33         <el-col :span="12">
34           <el-form-item label="时间粒度" prop="timeGranularity">
35             <el-select v-model="formData.timeGranularity" placeholder="请选择">
36               <el-option
37                 v-for="dict in getStrDictOptions(DICT_TYPE.TIME_GRANULARITY)"
38                 :key="dict.value"
39                 :label="dict.label"
40                 :value="dict.value"
41               />
42             </el-select>
43           </el-form-item>
44         </el-col>
45       </el-row>
46       <el-row>
331bbb 47         <el-col :span="6">
67ea85 48           <el-form-item label="指标精度" prop="precision">
J 49             <el-input v-model="formData.precision"/>
50           </el-form-item>
51         </el-col>
331bbb 52         <el-col :span="6">
67ea85 53           <el-form-item label="转换系数" prop="coefficient">
J 54             <el-input v-model="formData.coefficient"/>
55           </el-form-item>
56         </el-col>
331bbb 57         <el-col :span="6">
67ea85 58           <el-form-item label="数量单位" prop="unit">
J 59             <el-input v-model="formData.unit"/>
60           </el-form-item>
61         </el-col>
331bbb 62         <el-col :span="6">
J 63           <el-form-item label="固化标识" prop="solidifyFlag">
64             <el-select v-model="formData.solidifyFlag"
65                        clearable
66                        filterable
67                        allow-create
68                        placeholder="请选择">
69               <el-option
70                 v-for="dict in getStrDictOptions(DICT_TYPE.SOLIDIFY_FLAG)"
71                 :key="dict.value"
72                 :label="dict.label"
73                 :value="dict.value"
74               />
75             </el-select>
76           </el-form-item>
77         </el-col>
67ea85 78       </el-row>
J 79       <el-row>
80         <el-col :span="24">
81           <el-form-item label="备注" prop="remark">
82             <el-input v-model="formData.remark" type="textarea" maxlength="100"/>
83           </el-form-item>
84         </el-col>
85       </el-row>
86       <el-row>
87         <el-col :span="24">
88           <el-form-item label="表达式">
89             <el-table :data="expressionList" border style="width: 100%">
90               <el-table-column type="index" align="center" width="60" label="序号"/>
91               <el-table-column prop="" label="左括号" width="100" align="center">
92                 <template #default="scope">
93                   <el-input size="small" v-model="scope.row.parenthesesLeft" placeholder="" readonly maxlength="10"/>
94                   <el-button size="small" @click="addParenthesesLeft(scope.$index, scope.row)">+</el-button>
95                   <el-button size="small" @click="removeParenthesesLeft(scope.$index, scope.row)">-</el-button>
96                 </template>
97               </el-table-column>
98               <el-table-column prop="" label="指标" align="center">
99                 <template #default="scope">
100                   <el-select size="mini" v-model="scope.row.itemNo" filterable placeholder="请选择">
101                     <el-option v-for="(item, index) in itemList"
102                                :key="index"
103                                :label="item.itemName"
104                                :value="item.itemNo"/>
105                   </el-select>
106                 </template>
107               </el-table-column>
108               <el-table-column prop="" label="运算值" align="center">
109                 <template #default="scope">
110                   <el-input size="mini" v-model="scope.row.itemNo" placeholder="运算值" clearable/>
111                 </template>
112               </el-table-column>
113               <el-table-column prop="" label="右括号" width="100" align="center">
114                 <template #default="scope">
115                   <el-input size="small" v-model="scope.row.parenthesesRight" placeholder="" readonly/>
116                     <el-button size="small" @click="addParenthesesRight(scope.$index, scope.row, ')')">+
117                     </el-button>
118                     <el-button size="small" @click="removeParenthesesRight(scope.$index, scope.row)">-
119                     </el-button>
120                 </template>
121               </el-table-column>
122               <el-table-column prop="" label="运算符" width="100" align="center">
123                 <template #default="scope">
124                   <el-select size="mini" v-model="scope.row.operator" clearable placeholder="请选择"
125                              style="font-weight: 600;">
126                     <el-option v-for="item in operatorList"
127                                :key="item"
128                                :label="item"
129                                :value="item"/>
130                   </el-select>
131                 </template>
132               </el-table-column>
133               <el-table-column prop="" label="操作" width="100" align="center">
134                 <template #default="scope">
135                   <el-button @click="addExpressionRow(scope.$index, expressionList)" link type="primary" size="small">添加</el-button>
136                   <el-button @click="deleteExpressionRow(scope.$index, expressionList)" link type="danger" size="small">删除</el-button>
137                 </template>
138               </el-table-column>
139             </el-table>
140           </el-form-item>
141         </el-col>
142       </el-row>
143     </el-form>
144     <template #footer>
145       <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
146       <el-button @click="dialogVisible = false">取 消</el-button>
147     </template>
148   </Dialog>
149 </template>
150 <script lang="ts" setup>
151   import {DICT_TYPE, getStrDictOptions} from '@/utils/dict'
152   import * as DataSetApi from '@/api/data/ind/data/data.set'
153   import {CommonStatusEnum} from '@/utils/constants'
154   import * as DataSourceConfigApi from "@/api/infra/dataSourceConfig";
155   import * as ItemApi from '@/api/data/ind/item/item'
156   import { ElMessage } from 'element-plus'
157   import * as CategoryApi from '@/api/data/ind/category/index'
6a72da 158   import {handleTree} from "@/utils/tree";
67ea85 159
J 160   defineOptions({name: 'IndDataSetForm'})
161
162   const {t} = useI18n() // 国际化
163   const message = useMessage() // 消息弹窗
164
165   const dialogVisible = ref(false) // 弹窗的是否展示
166   const dialogTitle = ref('') // 弹窗的标题
167   const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
168   const formType = ref('') // 表单的类型:create - 新增;update - 修改
169   const itemList = ref([] as ItemApi.ItemVO[])
170   let formData = ref({
171     id: undefined,
172     itemNo: '',
173     itemName: '',
174     itemType: '',
175     itemCategory: '',
176     coefficient: '',
177     precision: '',
178     businessType: '',
179     timeRange: '',
180     timeGranularity: '',
181     remark: '',
4058b7 182     solidifyFlag:'',
67ea85 183     calItem: {
J 184       id: '',
185       expression: '',
186     }
187   })
188   let expressionList = ref([{
189     parenthesesLeft: '',
190     itemNo: '',
191     parenthesesRight: '',
192     operator: ''
193   }])
194
195   const validateAsNumber = (rule, value, callback) => {
196     const regex = /^(\-|\+)?\d+(\.\d+)?$/;
197     if (!regex.test(value)) {
198       callback(new Error('请输入数字!'));
199     }
200   }
201   const operatorList = ref(['+', '-', '*', '/', '&', '|', '!', '>', '<'])
202   const formRules = reactive({
203     itemName: [{required: true, message: '指标名称不能为空', trigger: 'blur'}],
47ee64 204     itemCategory: [{required: true, message: '指标类型不能为空', trigger: 'blur'}]
J 205     // precision: [{validator: validateAsNumber, trigger: 'blur' }],
206     // coefficient: [{validator: validateAsNumber, trigger: 'blur' }],
67ea85 207   })
J 208   const formRef = ref() // 表单 Ref
209   const dataSourceList = ref([] as DataSourceConfigApi.DataSourceConfigVO[])
210   const queryParams = reactive({})
6a72da 211
J 212   const dataCategoryList = ref<Tree[]>([])
213   const getCategoryTree = async () => {
214     dataCategoryList.value = []
215     const res = await CategoryApi.getCategoryListAllSimple()
216     let category: Tree = {id: 0, label: '主类目', children: []}
217     category.children = handleTree(res, 'id', 'pid')
218     dataCategoryList.value.push(category)
219   }
67ea85 220   /** 打开弹窗 */
J 221   const open = async (type: string, id?: number) => {
222     dialogVisible.value = true
223     dialogTitle.value = '复合指标'
224     formType.value = type
225     resetForm()
226
227     // 加载数据源列表
6a72da 228     await getCategoryTree()
67ea85 229     itemList.value = await ItemApi.getItemList(queryParams)
J 230     // 修改时,设置数据
231     if (id) {
232       formLoading.value = true
233       try {
234         formData.value = await ItemApi.getItem(id)
235         expressionList.value = []
236         let expression = formData.value.calItem.expression
237         do {
238           let indexArray = [
239             expression.indexOf('+'),
240             expression.indexOf('-'),
241             expression.indexOf('*'),
242             expression.indexOf('/'),
243             expression.indexOf('&'),
244             expression.indexOf('|'),
245             expression.indexOf('!'),
246             expression.indexOf('>'),
247             expression.indexOf('<')
248           ].sort(numAscSort)
249           if (indexArray[indexArray.length - 1] !== -1) {
250             let endIndex = 0
251             for (let key in indexArray) {
252               if (indexArray[key] > -1) {
253                 endIndex = indexArray[key]
254                 break
255               }
256             }
257             // 运算值
258             let itemNoStr = expression.substring(0, endIndex)
259
260             // 运算符
261             let operator = expression.substr(endIndex, 1)
262             let indexOfParenthesesLeft = itemNoStr.indexOf('(')
263             let lastIndexOfParenthesesLeft = itemNoStr.lastIndexOf('(')
264
265             // 左括号
266             let parenthesesLeft = ''
267             if (indexOfParenthesesLeft !== -1 && lastIndexOfParenthesesLeft !== -1) {
268               parenthesesLeft = itemNoStr.substring(indexOfParenthesesLeft, lastIndexOfParenthesesLeft + 1)
269               itemNoStr = itemNoStr.substring(lastIndexOfParenthesesLeft + 1)
270             }
271
272             let indexOfParenthesesRight = itemNoStr.indexOf(')')
273             let lastIndexOfParenthesesRight = itemNoStr.lastIndexOf(')')
274
275             // 右括号
276             let parenthesesRight = ''
277             if (indexOfParenthesesRight !== -1 && lastIndexOfParenthesesRight !== -1) {
278               parenthesesRight = itemNoStr.substring(indexOfParenthesesRight, lastIndexOfParenthesesRight + 1)
279               itemNoStr = itemNoStr.substring(0, indexOfParenthesesRight)
280             }
281             expressionList.value.push({
282               parenthesesLeft: parenthesesLeft,
283               itemNo: itemNoStr,
284               parenthesesRight: parenthesesRight,
285               operator: operator
286             })
287             expression = expression.substring(endIndex + 1)
288           } else {
289             let pointStr = expression
290             let indexOfParenthesesLeft = pointStr.indexOf('(')
291             let lastIndexOfParenthesesLeft = pointStr.lastIndexOf('(')
292             let parenthesesLeft = ''
293             if (indexOfParenthesesLeft !== -1 && lastIndexOfParenthesesLeft !== -1) {
294               parenthesesLeft = pointStr.substring(indexOfParenthesesLeft, lastIndexOfParenthesesLeft + 1)
295               pointStr = pointStr.substring(lastIndexOfParenthesesLeft + 1)
296             }
297             let indexOfParenthesesRight = pointStr.indexOf(')')
298             let lastIndexOfParenthesesRight = pointStr.lastIndexOf(')')
299             let parenthesesRight = ''
300             if (indexOfParenthesesRight !== -1 && lastIndexOfParenthesesRight !== -1) {
301               parenthesesRight = pointStr.substring(indexOfParenthesesRight, lastIndexOfParenthesesRight + 1)
302               pointStr = pointStr.substring(0, indexOfParenthesesRight)
303             }
304             expressionList.value.push({
305               parenthesesLeft: parenthesesLeft,
306               itemNo: pointStr,
307               parenthesesRight: parenthesesRight,
308               operator: ''
309             })
310             expression = ''
311           }
312         } while (expression && expression.length > 0)
313
314       } finally {
315         formLoading.value = false
316       }
317     }
318   }
319   function numAscSort(a, b) {
320     return a - b
321   }
322
323   defineExpose({open}) // 提供 open 方法,用于打开弹窗
324
325   /** 提交表单 */
326   const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
327   const submitForm = async () => {
328     // 校验表单
329     if (!formRef) return
330     const valid = await formRef.value.validate()
331     if (!valid) return
332     // 提交请求
333     formLoading.value = true
334     try {
335       if (expressionList.value && expressionList.value.length > 0) {
336         let parenthesesLeftRex = /^[(]*$/
337         let parenthesesRightRex = /^[)]*$/
338         let expression = ''
339         for (let i = 0; i < expressionList.value.length; i++) {
340           let value = expressionList.value[i]
341           if (!parenthesesLeftRex.test(value.parenthesesLeft)) {
342             ElMessage({
343               message: `第${i + 1}行左括号输入不正确!`,
344               type: 'error',
345               duration: 1500
346             })
347             return
348           }
349           if (!parenthesesRightRex.test(value.parenthesesRight)) {
350             ElMessage({
351               message: `第${i + 1}行右括号输入不正确!`,
352               type: 'error',
353               duration: 1500
354             })
355             return
356           }
357           if (i !== (expressionList.value.length - 1) && !value.operator) {
358             ElMessage({
359               message: `第${i + 1}行运算符不能为空!`,
360               type: 'error',
361               duration: 1500
362             })
363             return
364           }
365           expression = expression + value.parenthesesLeft + value.itemNo + value.parenthesesRight + (i === (expressionList.value.length - 1) ? '' : value.operator)
366         }
367         formData.value.calItem.expression = expression
368       } else {
369         ElMessage({
370           message: `表达式不可以为空`,
371           type: 'error',
372           duration: 1500
373         })
374         return
375       }
376       formData.value.itemType = 'CAL'
377       const data = formData.value as ItemApi.ItemVO
378       if (formType.value === 'create') {
379         await ItemApi.createItem(data)
380         message.success(t('common.createSuccess'))
381       } else {
382         await ItemApi.updateItem(data)
383         message.success(t('common.updateSuccess'))
384       }
385       dialogVisible.value = false
386       // 发送操作成功的事件
387       emit('success')
388     } finally {
389       formLoading.value = false
390     }
391   }
392
393   function addExpressionRow(index, rows) {
394     let row = JSON.parse(JSON.stringify(rows[index]))
395     rows.splice(index, 0, row)
396   }
397
398   function addParenthesesLeft(index, row) {
399     if (row.parenthesesLeft) {
400       row.parenthesesLeft = row.parenthesesLeft + '('
401     } else {
402       row.parenthesesLeft = '('
403     }
404   }
405
406   function removeParenthesesLeft(index, row) {
407     if (row.parenthesesLeft) {
408       row.parenthesesLeft = row.parenthesesLeft.substring(0, row.parenthesesLeft.length - 1)
409     } else {
410       row.parenthesesLeft = ''
411     }
412   }
413
414   function addParenthesesRight(index, row) {
415     if (row.parenthesesRight) {
416       row.parenthesesRight = row.parenthesesRight + ')'
417     } else {
418       row.parenthesesRight = ')'
419     }
420   }
421
422   function removeParenthesesRight(index, row) {
423     if (row.parenthesesRight) {
424       row.parenthesesRight = row.parenthesesRight.substring(0, row.parenthesesRight.length - 1)
425     } else {
426       row.parenthesesRight = ''
427     }
428   }
429
430   function deleteExpressionRow(index, rows) {
431     if (!rows || rows.length === 1) {
432       ElMessage({
433         message: '不能全部删除!',
434         type: 'error',
435         duration: 1500
436       })
437       return
438     }
439     rows.splice(index, 1)
440   }
441
442   /** 重置表单 */
443   const resetForm = () => {
444     formData.value = {
445       id: undefined,
446       itemNo: '',
447       itemName: '',
448       itemType: '',
449       itemCategory: '',
450       coefficient: '',
451       precision: '',
452       businessType: '',
453       timeRange: '',
454       timeGranularity: '',
455       remark: '',
456       calItem: {
457         id: '',
458         expression: '',
459       }}
460     formRef.value?.resetFields()
461   }
462 </script>