فهرست منبع

fix: 成本审核表校验添加

shiyanyu 4 هفته پیش
والد
کامیت
7c2535f348
1فایلهای تغییر یافته به همراه264 افزوده شده و 319 حذف شده
  1. 264 319
      src/views/costAudit/auditInfo/auditManage/costAudit.vue

+ 264 - 319
src/views/costAudit/auditInfo/auditManage/costAudit.vue

@@ -13,7 +13,7 @@
               <div
                 style="display: flex; justify-self: start; align-items: center"
               >
-                <div style="width: 120px">监审类别:</div>
+                <div style="width: 120px">监审类别123:</div>
                 <el-cascader
                   v-model="auditForm.catalogId"
                   :options="catalogListOptions"
@@ -116,7 +116,7 @@
 
     <el-table :data="costAuditData" style="width: 100%" border>
       <el-table-column
-        v-for="item in costAuditcolumn"
+        v-for="item in visibleCostAuditColumns"
         :key="item.prop"
         :prop="item.prop"
         :label="item.label"
@@ -130,7 +130,7 @@
           <span v-if="item.isDisplayOnly">{{ scope.row[item.prop] }}</span>
           <!-- 字符串类型输入框 -->
           <el-input
-            v-else-if="item.fieldType === 'string'"
+            v-else-if="item.fieldType === 'string' && !item.dictCode"
             v-model="scope.row[item.prop]"
             :placeholder="item.label"
             :disabled="item.disabled"
@@ -140,49 +140,116 @@
             @input="handleCellInput(scope.row, item)"
             @change="handleCellInput(scope.row, item)"
           ></el-input>
-          <!-- 整数类型输入框 -->
+          <!-- 整数类型输入框(校验:仅整数,长度限制) -->
           <el-input
             v-else-if="item.fieldType === 'integer'"
-            v-model.number="scope.row[item.prop]"
+            v-model="scope.row[item.prop]"
             :placeholder="item.label"
             :disabled="item.disabled"
-            :maxlength="item.fieldTypelen ? parseInt(item.fieldTypelen) : null"
-            show-word-limit
+            :maxlength="
+              computeNumberMaxlength({
+                totalLength: item.fieldTypelen,
+                decimalLength: 0,
+              })
+            "
             style="width: 100%"
-            type="number"
-            step="1"
-            @input="handleCellInput(scope.row, item)"
+            @input="
+              sanitizeNumberInput(scope.row, {
+                prop: item.prop,
+                totalLength: item.fieldTypelen,
+                decimalLength: 0,
+              })
+            "
+            @keypress.native="
+              onNumberKeypressNumeric($event, scope.row, {
+                prop: item.prop,
+                decimalLength: 0,
+              })
+            "
+            @keydown.native="
+              onNumberKeydownNumeric($event, scope.row, {
+                prop: item.prop,
+                decimalLength: 0,
+              })
+            "
+            @paste.native.prevent="
+              onNumberPasteNumeric($event, scope.row, {
+                prop: item.prop,
+                decimalLength: 0,
+              })
+            "
+            @compositionend.native="
+              onNumberCompositionEndNumeric(scope.row, {
+                prop: item.prop,
+                totalLength: item.fieldTypelen,
+                decimalLength: 0,
+              })
+            "
             @change="handleCellInput(scope.row, item)"
           ></el-input>
-          <!-- 小数类型输入框 -->
+          <!-- 小数类型输入框(校验:小数位长度和值长度) -->
           <el-input
             v-else-if="item.fieldType === 'double'"
-            v-model.number="scope.row[item.prop]"
+            v-model="scope.row[item.prop]"
             :placeholder="item.label"
             :disabled="item.disabled"
-            :maxlength="item.fieldTypelen ? parseInt(item.fieldTypelen) : null"
-            show-word-limit
+            :maxlength="
+              computeNumberMaxlength({
+                totalLength: item.fieldTypelen,
+                decimalLength: item.fieldTypenointlen,
+              })
+            "
             style="width: 100%"
-            type="number"
-            step="0.01"
-            @input="handleCellInput(scope.row, item)"
+            @input="
+              sanitizeNumberInput(scope.row, {
+                prop: item.prop,
+                totalLength: item.fieldTypelen,
+                decimalLength: item.fieldTypenointlen,
+              })
+            "
+            @keypress.native="
+              onNumberKeypressNumeric($event, scope.row, {
+                prop: item.prop,
+                decimalLength: item.fieldTypenointlen,
+              })
+            "
+            @keydown.native="
+              onNumberKeydownNumeric($event, scope.row, {
+                prop: item.prop,
+                decimalLength: item.fieldTypenointlen,
+              })
+            "
+            @paste.native.prevent="
+              onNumberPasteNumeric($event, scope.row, {
+                prop: item.prop,
+                decimalLength: item.fieldTypenointlen,
+              })
+            "
+            @compositionend.native="
+              onNumberCompositionEndNumeric(scope.row, {
+                prop: item.prop,
+                totalLength: item.fieldTypelen,
+                decimalLength: item.fieldTypenointlen,
+              })
+            "
             @change="handleCellInput(scope.row, item)"
           ></el-input>
-          <!-- 日期类型输入框 -->
+          <!-- 日期/日期时间(根据 format 自动选择) -->
           <el-date-picker
             v-else-if="item.fieldType === 'datetime'"
             v-model="scope.row[item.prop]"
             :placeholder="item.label"
             :disabled="item.disabled"
             style="width: 100%"
-            type="date"
-            value-format="yyyy-MM-dd"
+            :type="computeDateType(item)"
+            :format="computeDateFormat(item)"
+            :value-format="computeDateFormat(item)"
             @input="handleCellInput(scope.row, item)"
             @change="handleCellInput(scope.row, item)"
           ></el-date-picker>
           <!-- 字典类型下拉框 -->
           <el-select
-            v-else-if="item.isDict === 'true'"
+            v-else-if="item.dictCode"
             v-model="scope.row[item.prop]"
             :placeholder="item.label"
             :disabled="item.disabled"
@@ -192,9 +259,9 @@
           >
             <el-option
               v-for="dict in getDictOptions(item.dictCode)"
-              :key="dict.value"
-              :label="dict.label"
-              :value="dict.value"
+              :key="dict.key || dict.value"
+              :label="dict.name || dict.label"
+              :value="dict.key || dict.value"
             ></el-option>
           </el-select>
           <!-- 默认输入框 -->
@@ -254,7 +321,7 @@
     getVerifyTemplateDetail,
   } from '@/api/costVerifyManage'
   import { getDetail } from '@/api/auditInitiation'
-  import { catalogMixin } from '@/mixins/useDict'
+  import { catalogMixin, dictMixin } from '@/mixins/useDict'
   import { getByTypeKey } from '@/api/dictionaryManage'
   import {
     saveSingleRecordSurvey,
@@ -264,7 +331,7 @@
   } from '@/api/audit/survey'
   export default {
     name: 'CostAudit',
-    mixins: [catalogMixin],
+    mixins: [catalogMixin, dictMixin],
     props: {
       id: {
         type: [String, Number],
@@ -329,273 +396,21 @@
         // 成本审核数据
         costAuditData: [],
         project: {},
-        // 年份到各列prop的映射:{ '2025': { book: '...', audit: '...', approved: '...' } }
+        // 年份到各列prop的映射
         yearPropMap: {},
-        // 字典缓存
+        // 字典缓存(当未使用 dictMixin 批量加载时备用)
         dictCache: {},
       }
     },
-    watch: {
-      // 监听id变化
-      // id(newVal) {
-      //   if (newVal) {
-      //     this.$nextTick(() => {
-      //     })
-      //   }
-      // },
-      // selectedProject(newVal) {
-      //   if (newVal) {
-      //     this.$nextTick(() => {
-      //       // 获取项目的详情数据
-      //       this.getDetail()
-      //     })
-      //   }
-      // },
-    },
-    created() {
-      // if (this.selectedProject && this.selectedProject.projectId) {
-      //   // 获取项目的详情数据
-      //   this.getDetail()
-      // }
-      // this.handleGenerateTemplate1()
+    computed: {
+      visibleCostAuditColumns() {
+        const cols = Array.isArray(this.costAuditcolumn)
+          ? this.costAuditcolumn
+          : []
+        return cols.filter((c) => c && c.showVisible !== '0')
+      },
     },
     methods: {
-      getDetail() {
-        this.getActiveCostVerifyFormListByType()
-        this.getActiveCostVerifyFormList()
-        // 获取项目的详情数据
-        getDetail({ id: this.selectedProject.projectId }).then((res) => {
-          if (res.code === 200) {
-            this.project = res.value
-            this.auditForm.catalogId = res.value.catalogId
-            // this.auditForm.surveyTemplateId = '9368f1cf-77e7-49fe-8502-4a7a2da99668'
-            this.loadTemplateData()
-          }
-        })
-      },
-      // 获取所有模板类型为固定表的所有启用成本调查表数据
-      getActiveCostVerifyFormListByType() {
-        getActiveCostVerifyFormListByType().then((res) => {
-          if (res.code === 200) {
-            this.surveyFormList = res.value
-          }
-        })
-      },
-      // 获取所有状态为启用的成本核定表模板数据
-      getActiveCostVerifyFormList() {
-        getActiveCostVerifyFormList().then((res) => {
-          if (res.code === 200) {
-            this.auditFormList = res.value
-          }
-        })
-      },
-      // TemplateTypeChange
-      handleTemplateTypeChange(val) {
-        if (this.auditForm.templateType == '1') {
-          this.historyTemplate = ''
-        } else if (this.auditForm.templateType == '2') {
-          this.dataTable = ''
-        }
-      },
-      // 模板生成操作
-      handleGenerateTemplate() {
-        // 提示生成后不能修改
-        this.$confirm(
-          '生成后不能修改根据调查表生成还是根据历史核定模板生成,确定生成模板吗?',
-          '提示',
-          {
-            confirmButtonText: '确定',
-            cancelButtonText: '取消',
-            type: 'warning',
-          }
-        ).then(() => {
-          // 校验表单
-          this.$refs['auditForm'].validate((valid) => {
-            if (valid) {
-              if (this.auditForm.templateType == '1') {
-                if (!this.auditForm.dataTable) {
-                  this.$message.error('请选择成本调查表模板')
-                  return
-                }
-                this.generateFromSurveyTemplate()
-              } else if (this.auditForm.templateType == '2') {
-                if (!this.auditForm.historyTemplate) {
-                  this.$message.error('请选择历史核定模板')
-                  return
-                }
-                this.generateFromHistoryTemplate()
-              }
-              // this.$message({ type: 'success', message: '模板生成成功' })
-            } else {
-              this.$message.error('请填写表单数据')
-              return
-            }
-          })
-        })
-      },
-      // 根据调查表生成模板
-      async generateFromSurveyTemplate() {
-        try {
-          // 保存模板基本信息
-          let data = await generateCostVerifyForm({
-            catalogId: this.auditForm.catalogId,
-            templatename: this.auditForm.surveyTemplateName,
-            templateId: this.auditForm.dataTable,
-            taskId: this.selectedProject.taskId,
-          })
-          this.auditForm.surveyTemplateId = data.value
-            ? data.value.surveyTemplateId
-            : ''
-          // 设置uploadId
-          if (data.value) {
-            this.auditForm.uploadId =
-              data.value.id || data.value.templateId || ''
-          }
-          this.loadTemplateDataForEdit(this.auditForm.surveyTemplateId)
-        } catch (error) {
-          console.error('生成模板失败:', error)
-        } finally {
-        }
-      },
-      // 根据历史核定模板生成
-      async generateFromHistoryTemplate() {
-        // 保存模板基本信息
-        let data = await generateCostVerifyFormData({
-          catalogId: this.auditForm.catalogId,
-          templatename: this.auditForm.surveyTemplateName,
-          templateId: this.auditForm.historyTemplate,
-          taskId: this.selectedProject.taskId,
-        })
-        this.auditForm.surveyTemplateId = data.value
-          ? data.value.surveyTemplateId
-          : ''
-        this.loadTemplateDataForEdit(this.auditForm.surveyTemplateId)
-      },
-
-      // 回显数据用
-      async loadTemplateData(surveyTemplateId) {
-        // 先获取表格数据
-        const tableDataRes = await getCostFormVersionsByTemplateId({
-          taskId: this.selectedProject.taskId,
-          // surveyTemplateId: '',
-        })
-
-        // 处理表格数据
-        if (tableDataRes.code == 200) {
-          this.auditForm.surveyTemplateId =
-            tableDataRes.value.itemlist[0].surveyTemplateId
-          // 有数据后再获取表头数据
-          const tableHeadersRes = await getlistBySurveyTemplateId({
-            // taskId: this.selectedProject.taskId,
-            surveyTemplateId: this.auditForm.surveyTemplateId,
-          })
-          getVerifyTemplateDetail({
-            id: this.auditForm.surveyTemplateId,
-          }).then((res) => {
-            this.auditForm.surveyTemplateName = res.value.surveyTemplateName
-            this.auditForm.catalogId = res.value.catalogId
-            this.auditForm.templateType = res.value.createmode
-            if (res.value.createmode == '1') {
-              this.auditForm.dataTable = res.value.createtemplateid
-            } else if (res.value.createmode == '2') {
-              this.auditForm.historyTemplate = res.value.createtemplateid
-            }
-            // 设置uploadId以便数据回显
-            this.auditForm.uploadId = res.value.id || res.value.templateId || ''
-          })
-
-          // 处理表头数据
-          if (tableHeadersRes.code == 200) {
-            this.parseAndDisplayTableHeaders(tableHeadersRes)
-            this.parseAndDisplayTableData(tableDataRes)
-            await this.tryEchoUploadData()
-          }
-        }
-      },
-      async loadTemplateDataForEdit(surveyTemplateId) {
-        // 并行获取表头和表格数据
-        const [tableHeadersRes, tableDataRes] = await Promise.all([
-          getlistBySurveyTemplateId({
-            taskId: this.selectedProject.taskId,
-            surveyTemplateId: this.auditForm.surveyTemplateId,
-          }),
-          getCostFormVersionsByTemplateId({
-            taskId: this.selectedProject.taskId,
-            surveyTemplateId: this.auditForm.surveyTemplateId,
-          }),
-        ])
-        // 处理表头数据
-        if (tableHeadersRes.code == 200) {
-          this.parseAndDisplayTableHeaders(tableHeadersRes)
-        }
-        // 处理表格数据
-        if (tableDataRes.code == 200) {
-          // 在这里也设置uploadId
-          getVerifyTemplateDetail({
-            id: this.auditForm.surveyTemplateId,
-          }).then((res) => {
-            if (res && res.code === 200 && res.value) {
-              this.auditForm.uploadId =
-                res.value.id || res.value.templateId || ''
-            }
-          })
-          this.parseAndDisplayTableData(tableDataRes)
-          await this.tryEchoUploadData()
-        }
-      },
-      // 根据已存在的上传数据进行回显(若有)
-      async tryEchoUploadData() {
-        try {
-          const uploadId = (this.auditForm && this.auditForm.uploadId) || ''
-          const auditedUnitId = this.auditedUnitId || ''
-          const taskId =
-            (this.selectedProject && this.selectedProject.taskId) || ''
-          // if (!uploadId || !auditedUnitId) return
-          const params = {
-            // uploadId,
-            // auditedUnitId,
-            type: '3',
-            taskId: taskId,
-          }
-          const res = await getSurveyDetail(params)
-          if (res && res.code === 200 && res.value) {
-            const triplets = Array.isArray(res.value)
-              ? res.value
-              : res.value.items || res.value.records || []
-            if (!Array.isArray(triplets) || triplets.length === 0) return
-            // 建立字段名映射:rkey(中文列名) -> fieldEname(表格prop)
-            const headerMap = {}
-            ;(this.tableHeadersRes || []).forEach((h) => {
-              if (h && h.fieldName && h.fieldEname)
-                headerMap[h.fieldName] = h.fieldEname
-            })
-            // 行索引:rowid -> 行对象
-            const rowMap = new Map()
-            ;(this.costAuditData || []).forEach((row) => {
-              if (row && row.rowid !== undefined && row.rowid !== null) {
-                rowMap.set(String(row.rowid), row)
-              }
-            })
-            // 应用回显
-            triplets.forEach((item) => {
-              const rowid = item.rowid || item.rowId || item.ROWID
-              const rkey = item.rkey || item.rKey || item.RKEY
-              const rvalue = item.rvalue || item.rValue || item.RVALUE
-              if (!rowid || !rkey) return
-              const row = rowMap.get(String(rowid))
-              const prop = headerMap[rkey]
-              if (row && prop) {
-                row[prop] = rvalue != null ? String(rvalue) : ''
-              }
-            })
-            // 强制刷新
-            this.costAuditData = [...this.costAuditData]
-            this.computeApprovedForAllRows()
-          }
-        } catch (e) {
-          console.warn('回显上传数据失败:', e)
-        }
-      },
       // 单元格输入联动:当账面值或审核调整值变化时,自动计算核定值
       handleCellInput(row, item) {
         if (!row) return
@@ -657,7 +472,7 @@
         if (!Array.isArray(this.costAuditData)) return
         this.costAuditData.forEach((row) => this.computeApprovedForRow(row))
       },
-      parseAndDisplayTableHeaders(res) {
+      async parseAndDisplayTableHeaders(res) {
         this.tableHeadersRes = Array.isArray(res.value) ? res.value : []
         if (this.tableHeadersRes.length > 0) {
           // 重置年份映射
@@ -666,6 +481,7 @@
           // 表头按照orderNum重新排序
           this.tableHeadersRes.sort((a, b) => a.orderNum - b.orderNum)
           this.costAuditcolumn = [] // 清空现有列配置
+          const dictCodes = new Set()
           this.tableHeadersRes.forEach((item) => {
             let column = {
               ...item,
@@ -683,6 +499,11 @@
             }
             this.costAuditcolumn.push(column)
 
+            // 收集字典编码,统一加载
+            if (column && column.dictCode) {
+              dictCodes.add(String(column.dictCode))
+            }
+
             // 基于表头中文名构建年份映射
             const name = item.fieldName || item.label || ''
             const prop = item.fieldEname || item.prop || ''
@@ -699,6 +520,17 @@
               else if (kind === '核定值') this.yearPropMap[year].approved = prop
             }
           })
+          // 统一初始化并加载字典(等待加载完成,避免首次渲染下拉为空)
+          if (dictCodes.size > 0) {
+            if (!this.dictData) this.dictData = {}
+            dictCodes.forEach((code) => {
+              if (!this.dictData[code]) this.$set(this.dictData, code, [])
+            })
+            if (typeof this.getDictType === 'function') {
+              await this.getDictType()
+            }
+          }
+
           // 若表头未包含“单位”列,则追加;已包含则不重复添加
           const hasUnitCol = this.costAuditcolumn.some(
             (c) => c && (c.label === '单位' || c.fieldName === '单位')
@@ -1585,43 +1417,156 @@
           }
         )
       },
-      // 获取字典选项
-      async getDictOptions(dictCode) {
-        // 如果没有字典代码,返回空数组
-        if (!dictCode) {
-          return []
+      // 获取字典选项(由 dictMixin.getDictType 维护 dictData)
+      getDictOptions(dictCode) {
+        if (!dictCode || !this.dictData) return []
+        const arr = this.dictData[String(dictCode)]
+        return Array.isArray(arr) ? arr : []
+      },
+      // -------------- 数字/日期输入校验(对齐 FixedTableDialog 思路) --------------
+      computeNumberMaxlength(cfg) {
+        const intLimit = Number(cfg && cfg.totalLength)
+        const decLimit = Number(cfg && cfg.decimalLength)
+        if (!isNaN(intLimit) && intLimit > 0) {
+          const extra = !isNaN(decLimit) && decLimit > 0 ? 1 + decLimit : 0
+          // 预留一个符号位
+          return intLimit + extra + 1
         }
-
-        // 检查是否已经缓存了该字典数据
-        if (this.dictCache && this.dictCache[dictCode]) {
-          return this.dictCache[dictCode]
+        return undefined
+      },
+      sanitizeNumberInput(row, cfg) {
+        const prop = cfg && cfg.prop
+        const totalLen = Number(cfg && cfg.totalLength)
+        const decLen = Number(cfg && cfg.decimalLength)
+        if (!row || !prop) return
+        let v = row[prop]
+        if (v === null || v === undefined) {
+          row[prop] = ''
+          return
         }
-
-        try {
-          // 从后端获取字典数据
-          const response = await getByTypeKey({ typeKey: dictCode })
-          let dictOptions = []
-
-          // 处理响应数据
-          if (response && Array.isArray(response)) {
-            dictOptions = response.map((item) => ({
-              value: item.dictValue || item.key || item.value,
-              label: item.dictLabel || item.name || item.label,
-            }))
+        v = String(v)
+        // 仅保留数字、+、-、.
+        v = v.replace(/[^0-9+\-.]/g, '')
+        // 只允许一个正负号且在最前
+        v = v.replace(/(?!^)[+\-]/g, '')
+        // 小数位限制
+        const sign = v.startsWith('-') || v.startsWith('+') ? v[0] : ''
+        let unsigned = sign ? v.slice(1) : v
+        // 处理多个小数点
+        const parts = unsigned.split('.')
+        if (parts.length > 2)
+          unsigned = parts[0] + '.' + parts.slice(1).join('')
+        let iRaw, dRaw
+        if (unsigned.includes('.')) {
+          ;[iRaw, dRaw = ''] = unsigned.split('.')
+        } else {
+          iRaw = unsigned
+          dRaw = undefined
+        }
+        let i = (iRaw || '').replace(/^0+(?=\d)/, '')
+        let d = dRaw || ''
+        if (!isNaN(totalLen) && totalLen > 0 && i.length > totalLen) {
+          i = i.slice(0, totalLen)
+        }
+        if (!isNaN(decLen) && decLen >= 0 && d.length > decLen) {
+          d = d.slice(0, decLen)
+        }
+        if (!isNaN(decLen) && decLen === 0) {
+          v = sign + i
+        } else if (dRaw !== undefined) {
+          v = sign + (i || '0') + '.' + d
+        } else {
+          v = sign + i
+        }
+        if (v === '-' || v === '+') {
+          row[prop] = ''
+          return
+        }
+        row[prop] = v
+      },
+      onNumberKeypressNumeric(e, row, cfg) {
+        const decLimit = Number(cfg && cfg.decimalLength)
+        const ch = e.key
+        if (ch.length !== 1) return
+        if (/[0-9]/.test(ch)) return
+        if (ch === '.') {
+          if (isNaN(decLimit) || decLimit <= 0) {
+            e.preventDefault()
+            return
           }
-
-          // 缓存字典数据
-          if (!this.dictCache) {
-            this.dictCache = {}
+          const prop = cfg && cfg.prop
+          const val = String((prop && row[prop]) || '')
+          if (val.includes('.')) e.preventDefault()
+          return
+        }
+        if (ch === '-' || ch === '+') {
+          const prop = cfg && cfg.prop
+          const val = String((prop && row[prop]) || '')
+          if (val.length > 0) e.preventDefault()
+          return
+        }
+        e.preventDefault()
+      },
+      onNumberKeydownNumeric(e) {
+        const code = e.key
+        const allowed = [
+          'Backspace',
+          'Delete',
+          'Tab',
+          'ArrowLeft',
+          'ArrowRight',
+          'Home',
+          'End',
+        ]
+        if (allowed.includes(code)) return
+      },
+      onNumberPasteNumeric(e, row, cfg) {
+        const text = (e.clipboardData && e.clipboardData.getData('text')) || ''
+        if (!text) return
+        const decLimit = Number(cfg && cfg.decimalLength)
+        let v = text.replace(/[^0-9+\-.]/g, '')
+        v = v.replace(/(?!^)[+\-]/g, '')
+        if (!isNaN(decLimit) && decLimit === 0) {
+          v = v.replace(/[.]/g, '')
+        } else {
+          const ps = v.split('.')
+          if (ps.length > 2) v = ps[0] + '.' + ps.slice(1).join('')
+        }
+        const prop = cfg && cfg.prop
+        if (!prop) return
+        row[prop] = v
+        this.sanitizeNumberInput(row, cfg)
+      },
+      onNumberCompositionEndNumeric(row, cfg) {
+        this.sanitizeNumberInput(row, cfg)
+      },
+      // 父组件在切换到“成本审核”Tab时会调用该方法
+      // 这里做安全兜底:若已有模板ID则加载数据;否则静默返回,等待上游选择后再加载
+      getDetail() {
+        const sid = this.auditForm && this.auditForm.surveyTemplateId
+        if (sid) {
+          // 优先使用回显加载流程
+          if (typeof this.loadTemplateData === 'function') {
+            this.loadTemplateData(sid)
           }
-          this.dictCache[dictCode] = dictOptions
-
-          return dictOptions
-        } catch (error) {
-          console.error('获取字典数据失败:', error)
-          return []
         }
       },
+      computeDateType(item) {
+        const fmt =
+          item && item.format ? String(item.format).trim().toLowerCase() : ''
+        if (fmt.includes('h')) return 'datetime'
+        if (/^y{4}$/.test(fmt)) return 'year'
+        if (fmt.includes('yyyy-mm')) return 'date'
+        // 没有format时,根据字段类型默认日期
+        return 'date'
+      },
+      computeDateFormat(item) {
+        const t = this.computeDateType(item)
+        const fmt = item && item.format ? String(item.format).trim() : ''
+        if (t === 'datetime') return fmt || 'yyyy-MM-dd HH:mm:ss'
+        if (t === 'year') return fmt || 'yyyy'
+        return fmt || 'yyyy-MM-dd'
+      },
     },
   }
 </script>