shiyanyu 1 месяц назад
Родитель
Сommit
6b2afd6649

+ 226 - 17
src/views/EntDeclaration/auditTaskManagement/components/FixedTableDialog.vue

@@ -83,10 +83,12 @@
           <el-input
             v-if="!scope.row.isCategory"
             v-model="scope.row[`year_${year}`]"
+            inputmode="decimal"
             :placeholder="`请输入${year}年数据`"
             :disabled="isViewMode"
+            style="width: 100%"
+            @input="handleNumericInput(scope.row, year, $event)"
             @blur="handleCellBlur(scope.row, year)"
-            @input="handleCellInput(scope.row, year)"
           />
         </template>
       </el-table-column>
@@ -651,6 +653,46 @@
         // 实时联动计算
         this.recomputeDependentsForRow(row, year)
       },
+      // 仅数字输入:按行规则限制小数位;整数不允许小数
+      handleNumericInput(row, year, val) {
+        if (!row) return
+        const rules = (row && row.validateRules) || {}
+        const field = `year_${year}`
+        let s = String(val == null ? '' : val)
+        // 允许负号和小数点,其余去除
+        s = s.replace(/[^0-9+\-\.]/g, '')
+        // 只保留第一个负号在最前
+        s = s.replace(/(?!^)-/g, '')
+        if (s.startsWith('+')) s = s.slice(1)
+        // 多个点只保留第一个
+        const firstDot = s.indexOf('.')
+        if (firstDot >= 0) {
+          s =
+            s.slice(0, firstDot + 1) + s.slice(firstDot + 1).replace(/\./g, '')
+        }
+        const t = String(rules.type || '').toLowerCase()
+        const isInteger =
+          t === 'integer' ||
+          (t === 'number' &&
+            (rules.fieldTypenointlen === 0 || rules.decimalLength === 0))
+        if (isInteger && s.includes('.')) {
+          s = s.split('.')[0]
+        }
+        if (!isInteger) {
+          const dlen =
+            rules.fieldTypenointlen ||
+            rules.fieldTypeNointLen ||
+            rules.decimalLength
+          if (typeof dlen === 'number' && dlen >= 0 && firstDot >= 0) {
+            const parts = s.split('.')
+            parts[1] = (parts[1] || '').slice(0, dlen)
+            s = parts.join('.')
+          }
+        }
+        // 写回且触发联动
+        this.$set(row, field, s)
+        this.handleCellInput(row, year)
+      },
       // 单元格失焦事件
       handleCellBlur(row, year) {
         // 验证格式和非空
@@ -675,37 +717,133 @@
           return false
         }
 
-        // 格式验证
-        if (value && row.validateRules) {
-          if (row.validateRules.type === 'number') {
-            const numValue = Number(value)
-            if (isNaN(numValue)) {
+        // 类型/规则验证:支持 integer/decimal/number/boolean/date/dict
+        if (
+          value !== undefined &&
+          value !== null &&
+          value !== '' &&
+          row.validateRules
+        ) {
+          const r = row.validateRules || {}
+          const rawStr = String(value).trim()
+          const type = String(r.type || '').toLowerCase()
+
+          // 数值范围通用检查
+          const checkRange = (numVal) => {
+            if (r.min !== undefined && numVal < r.min) {
               this.showFieldError(
                 row,
                 year,
-                `${row.itemName}的${year}年数据必须是数字`
+                `${row.itemName}的${year}年数据不能小于${r.min}`
               )
               return false
             }
-            if (
-              row.validateRules.min !== undefined &&
-              numValue < row.validateRules.min
-            ) {
+            if (r.max !== undefined && numVal > r.max) {
               this.showFieldError(
                 row,
                 year,
-                `${row.itemName}的${year}年数据不能小于${row.validateRules.min}`
+                `${row.itemName}的${year}年数据不能大于${r.max}`
               )
               return false
             }
-            if (
-              row.validateRules.max !== undefined &&
-              numValue > row.validateRules.max
-            ) {
+            return true
+          }
+
+          if (
+            type === 'integer' ||
+            (type === 'number' &&
+              (r.fieldTypenointlen === 0 || r.decimalLength === 0))
+          ) {
+            // 仅整数;可选长度限制 fieldTypelen/fieldTypelen 别名
+            const intPattern = /^-?\d+$/
+            if (!intPattern.test(rawStr)) {
+              this.showFieldError(
+                row,
+                year,
+                `${row.itemName}的${year}年数据必须为整数`
+              )
+              return false
+            }
+            const limit = r.fieldTypelen || r.fieldTypeLen || r.totalLength
+            if (limit && rawStr.replace('-', '').length > Number(limit)) {
+              this.showFieldError(
+                row,
+                year,
+                `${row.itemName}的${year}年整数长度不能超过${limit}位`
+              )
+              return false
+            }
+            const numVal = Number(rawStr)
+            if (!checkRange(numVal)) return false
+          } else if (
+            type === 'decimal' ||
+            type === 'double' ||
+            type === 'float' ||
+            type === 'number'
+          ) {
+            const numVal = Number(rawStr)
+            if (Number.isNaN(numVal)) {
+              this.showFieldError(
+                row,
+                year,
+                `${row.itemName}的${year}年数据必须为数字`
+              )
+              return false
+            }
+            const dlen =
+              r.fieldTypenointlen || r.fieldTypeNointLen || r.decimalLength
+            if (dlen !== undefined && dlen !== null) {
+              const decimals = rawStr.split('.')[1] || ''
+              if (decimals.length > Number(dlen)) {
+                this.showFieldError(
+                  row,
+                  year,
+                  `${row.itemName}的${year}年数据小数位不能超过${dlen}位`
+                )
+                return false
+              }
+            }
+            if (!checkRange(numVal)) return false
+          } else if (type === 'boolean' || type === 'bool') {
+            const ok = [
+              'true',
+              'false',
+              '1',
+              '0',
+              '是',
+              '否',
+              'Y',
+              'N',
+              'y',
+              'n',
+            ].includes(rawStr)
+            if (!ok) {
+              this.showFieldError(
+                row,
+                year,
+                `${row.itemName}的${year}年数据必须为布尔值(是/否/true/false/1/0)`
+              )
+              return false
+            }
+          } else if (type === 'date' || type === 'datetime' || r.dateFormat) {
+            const fmt =
+              r.dateFormat ||
+              (type === 'datetime' ? 'yyyy-MM-dd HH:mm:ss' : 'yyyy-MM-dd')
+            if (!this.validateDateByFormat(rawStr, fmt)) {
+              this.showFieldError(
+                row,
+                year,
+                `${row.itemName}的${year}年数据不符合日期格式 ${fmt}`
+              )
+              return false
+            }
+          } else if (type === 'dict' || r.allowedValues) {
+            const arr = Array.isArray(r.allowedValues) ? r.allowedValues : []
+            if (arr.length > 0 && !arr.includes(rawStr)) {
               this.showFieldError(
                 row,
                 year,
-                `${row.itemName}的${year}年数据不能大于${row.validateRules.max}`
+                `${row.itemName}的${year}年数据必须为字典项之一`
               )
               return false
             }
@@ -810,6 +948,77 @@
           })
         })
       },
+      // 简单日期校验(支持 yyyy-MM-dd 与 yyyy-MM-dd HH:mm:ss)
+      validateDateByFormat(value, format) {
+        const v = String(value).trim()
+        if (!v) return false
+        if (format === 'yyyy-MM-dd') {
+          const m = v.match(/^(\d{4})-(\d{2})-(\d{2})$/)
+          if (!m) return false
+          const y = +m[1],
+            mo = +m[2],
+            d = +m[3]
+          const dt = new Date(y, mo - 1, d)
+          return (
+            dt.getFullYear() === y &&
+            dt.getMonth() === mo - 1 &&
+            dt.getDate() === d
+          )
+        }
+        if (format === 'yyyy-MM-dd HH:mm:ss') {
+          const m = v.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/)
+          if (!m) return false
+          const y = +m[1],
+            mo = +m[2],
+            d = +m[3],
+            hh = +m[4],
+            mm = +m[5],
+            ss = +m[6]
+          const dt = new Date(y, mo - 1, d, hh, mm, ss)
+          return (
+            dt.getFullYear() === y &&
+            dt.getMonth() === mo - 1 &&
+            dt.getDate() === d &&
+            dt.getHours() === hh &&
+            dt.getMinutes() === mm &&
+            dt.getSeconds() === ss
+          )
+        }
+        // 其它格式简单兜底:只要不是空
+        return !!v
+      },
+      // ===== 数字输入控件参数推导 =====
+      getPrecision(row) {
+        const r = (row && row.validateRules) || {}
+        const t = String(r.type || '').toLowerCase()
+        // integer 或 number 且小数位为 0
+        if (
+          t === 'integer' ||
+          (t === 'number' &&
+            (r.fieldTypenointlen === 0 || r.decimalLength === 0))
+        ) {
+          return 0
+        }
+        const d = r.fieldTypenointlen || r.fieldTypeNointLen || r.decimalLength
+        if (d === 0) return 0
+        if (typeof d === 'number') return d
+        return undefined
+      },
+      getStep(row) {
+        const p = this.getPrecision(row)
+        if (p === 0) return 1
+        if (typeof p === 'number' && p > 0)
+          return Number((1 / Math.pow(10, p)).toFixed(p))
+        return 1
+      },
+      getMin(row) {
+        const r = (row && row.validateRules) || {}
+        return typeof r.min === 'number' ? r.min : undefined
+      },
+      getMax(row) {
+        const r = (row && row.validateRules) || {}
+        return typeof r.max === 'number' ? r.max : undefined
+      },
       // 关闭弹窗
       handleClose() {
         this.dialogVisible = false