|
|
@@ -19,7 +19,7 @@
|
|
|
|
|
|
<!-- 动态表头列 -->
|
|
|
<el-table-column
|
|
|
- v-for="column in dynamicColumns"
|
|
|
+ v-for="column in renderedColumns"
|
|
|
:key="column.prop"
|
|
|
:prop="column.prop"
|
|
|
:label="column.label"
|
|
|
@@ -27,22 +27,8 @@
|
|
|
:align="column.align || 'center'"
|
|
|
>
|
|
|
<template slot-scope="scope">
|
|
|
- <!-- parentid 为 -1 的行只读显示 -->
|
|
|
- <span
|
|
|
- v-if="
|
|
|
- scope.row.parentid === '-1' ||
|
|
|
- scope.row.parentid === -1 ||
|
|
|
- scope.row.parentId === '-1' ||
|
|
|
- scope.row.parentId === -1 ||
|
|
|
- scope.row.isCategory
|
|
|
- "
|
|
|
- class="category-value"
|
|
|
- >
|
|
|
- {{ scope.row[column.prop] || '-' }}
|
|
|
- </span>
|
|
|
- <!-- 子项可编辑 -->
|
|
|
<el-date-picker
|
|
|
- v-else-if="column.type === 'date'"
|
|
|
+ v-if="column.type === 'date'"
|
|
|
v-model="scope.row[column.prop]"
|
|
|
type="date"
|
|
|
placeholder="选择日期"
|
|
|
@@ -52,6 +38,81 @@
|
|
|
style="width: 100%"
|
|
|
:disabled="isViewMode"
|
|
|
/>
|
|
|
+ <el-date-picker
|
|
|
+ v-else-if="column.type === 'datetime'"
|
|
|
+ v-model="scope.row[column.prop]"
|
|
|
+ type="datetime"
|
|
|
+ placeholder="选择日期时间"
|
|
|
+ size="mini"
|
|
|
+ format="yyyy-MM-dd HH:mm:ss"
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isViewMode"
|
|
|
+ />
|
|
|
+ <el-date-picker
|
|
|
+ v-else-if="column.type === 'year'"
|
|
|
+ v-model="scope.row[column.prop]"
|
|
|
+ type="year"
|
|
|
+ placeholder="选择年份"
|
|
|
+ size="mini"
|
|
|
+ format="yyyy"
|
|
|
+ value-format="yyyy"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isViewMode"
|
|
|
+ />
|
|
|
+ <el-select
|
|
|
+ v-else-if="
|
|
|
+ column.type === 'select' && Array.isArray(column.options)
|
|
|
+ "
|
|
|
+ v-model="scope.row[column.prop]"
|
|
|
+ :placeholder="column.placeholder || '请选择' + column.label"
|
|
|
+ size="mini"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isViewMode"
|
|
|
+ clearable
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="opt in column.options"
|
|
|
+ :key="
|
|
|
+ opt &&
|
|
|
+ (opt.value != null ? String(opt.value) : String(opt.label))
|
|
|
+ "
|
|
|
+ :label="opt && (opt.label != null ? opt.label : opt.value)"
|
|
|
+ :value="
|
|
|
+ opt &&
|
|
|
+ (opt.value != null ? String(opt.value) : String(opt.label))
|
|
|
+ "
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <el-switch
|
|
|
+ v-else-if="column.type === 'boolean'"
|
|
|
+ v-model="scope.row[column.prop]"
|
|
|
+ :disabled="isViewMode"
|
|
|
+ active-value="1"
|
|
|
+ inactive-value="0"
|
|
|
+ active-text="是"
|
|
|
+ inactive-text="否"
|
|
|
+ />
|
|
|
+ <el-input
|
|
|
+ v-else-if="column.type === 'number'"
|
|
|
+ v-model="scope.row[column.prop]"
|
|
|
+ :placeholder="column.placeholder || '请输入' + column.label"
|
|
|
+ size="mini"
|
|
|
+ :disabled="isViewMode"
|
|
|
+ :maxlength="computeNumberMaxlength(column)"
|
|
|
+ @input="sanitizeNumberInput(scope.row, column)"
|
|
|
+ @keypress.native="
|
|
|
+ onNumberKeypressNumeric($event, scope.row, column)
|
|
|
+ "
|
|
|
+ @keydown.native="onNumberKeydownNumeric($event, scope.row, column)"
|
|
|
+ @paste.native.prevent="
|
|
|
+ onNumberPasteNumeric($event, scope.row, column)
|
|
|
+ "
|
|
|
+ @compositionend.native="
|
|
|
+ onNumberCompositionEndNumeric(scope.row, column)
|
|
|
+ "
|
|
|
+ @blur="handleCellBlur(scope.row, column.prop)"
|
|
|
+ />
|
|
|
<el-input
|
|
|
v-else
|
|
|
v-model="scope.row[column.prop]"
|
|
|
@@ -170,6 +231,11 @@
|
|
|
type: [String, Number],
|
|
|
default: '',
|
|
|
},
|
|
|
+ // 字段元数据(从模板接口返回,包含校验信息)
|
|
|
+ columnsMeta: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [],
|
|
|
+ },
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
@@ -224,6 +290,65 @@
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
+ // 根据后端字段的 fieldType/规则渲染具体控件类型
|
|
|
+ renderedColumns() {
|
|
|
+ const cols = Array.isArray(this.dynamicColumns)
|
|
|
+ ? this.dynamicColumns.map((c) => ({ ...c }))
|
|
|
+ : []
|
|
|
+ const metas = Array.isArray(this.columnsMeta) ? this.columnsMeta : []
|
|
|
+ const byLabel = new Map()
|
|
|
+ const byId = new Map()
|
|
|
+ metas.forEach((m) => {
|
|
|
+ if (m && m.label) byLabel.set(String(m.label), m)
|
|
|
+ if (m && m.fieldId) byId.set(String(m.fieldId), m)
|
|
|
+ })
|
|
|
+ const normalizeType = (meta, fallback) => {
|
|
|
+ const t = (
|
|
|
+ meta && (meta.type || meta.fieldType)
|
|
|
+ ? String(meta.type || meta.fieldType)
|
|
|
+ : ''
|
|
|
+ ).toLowerCase()
|
|
|
+ if (!t) return fallback
|
|
|
+ if (t.includes('datetime') || t.includes('timestamp'))
|
|
|
+ return 'datetime'
|
|
|
+ if (t.includes('date') && !t.includes('datetime')) return 'date'
|
|
|
+ if (t.includes('year')) return 'year'
|
|
|
+ if (
|
|
|
+ t.includes('int') ||
|
|
|
+ t.includes('number') ||
|
|
|
+ t.includes('decimal') ||
|
|
|
+ t.includes('float') ||
|
|
|
+ t.includes('double')
|
|
|
+ )
|
|
|
+ return 'number'
|
|
|
+ if (t.includes('bool')) return 'boolean'
|
|
|
+ if (t.includes('select')) return 'select'
|
|
|
+ return fallback
|
|
|
+ }
|
|
|
+ return cols.map((col) => {
|
|
|
+ const meta =
|
|
|
+ (col.label && byLabel.get(String(col.label))) ||
|
|
|
+ (col.fieldId && byId.get(String(col.fieldId))) ||
|
|
|
+ null
|
|
|
+ if (meta) {
|
|
|
+ col.type = normalizeType(meta, col.type || 'input')
|
|
|
+ if (Array.isArray(meta.options) && meta.options.length) {
|
|
|
+ col.options = meta.options
|
|
|
+ }
|
|
|
+ col.totalLength = meta.totalLength
|
|
|
+ // 若为整数类型,强制小数位为0
|
|
|
+ const rawType = String(
|
|
|
+ meta.type || meta.fieldType || ''
|
|
|
+ ).toLowerCase()
|
|
|
+ if (rawType.includes('int') || rawType === 'integer') {
|
|
|
+ col.decimalLength = 0
|
|
|
+ } else {
|
|
|
+ col.decimalLength = meta.decimalLength
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return col
|
|
|
+ })
|
|
|
+ },
|
|
|
},
|
|
|
watch: {
|
|
|
tableItems: {
|
|
|
@@ -294,6 +419,136 @@
|
|
|
},
|
|
|
},
|
|
|
methods: {
|
|
|
+ sanitizeNumberInput(row, column) {
|
|
|
+ const prop = column && column.prop
|
|
|
+ if (!prop) return
|
|
|
+ let v = row[prop]
|
|
|
+ if (v === null || v === undefined) {
|
|
|
+ row[prop] = ''
|
|
|
+ return
|
|
|
+ }
|
|
|
+ v = String(v)
|
|
|
+ // 仅保留数字、-、.,并处理多个符号
|
|
|
+ v = v.replace(/[^0-9+\-.]/g, '')
|
|
|
+ // 只允许一个正负号且在最前
|
|
|
+ v = v.replace(/(?!^)[+\-]/g, '')
|
|
|
+ // 只允许一个小数点
|
|
|
+ const parts = v.split('.')
|
|
|
+ if (parts.length > 2) {
|
|
|
+ v = parts[0] + '.' + parts.slice(1).join('')
|
|
|
+ }
|
|
|
+ // 限制整数/小数位
|
|
|
+ const intLimit = Number(column.totalLength)
|
|
|
+ const decLimit = Number(column.decimalLength)
|
|
|
+ const sign = v.startsWith('-') || v.startsWith('+') ? v[0] : ''
|
|
|
+ const unsigned = sign ? v.slice(1) : v
|
|
|
+ const [iRaw, dRaw = ''] = unsigned.split('.')
|
|
|
+ let i = iRaw.replace(/^0+(?=\d)/, '')
|
|
|
+ let d = dRaw
|
|
|
+ if (!isNaN(intLimit) && intLimit > 0 && i.length > intLimit) {
|
|
|
+ i = i.slice(0, intLimit)
|
|
|
+ }
|
|
|
+ if (!isNaN(decLimit) && decLimit >= 0 && d.length > decLimit) {
|
|
|
+ d = d.slice(0, decLimit)
|
|
|
+ }
|
|
|
+ if (!isNaN(decLimit) && decLimit === 0) {
|
|
|
+ // 整数:不保留小数点
|
|
|
+ v = sign + i
|
|
|
+ } else {
|
|
|
+ v = sign + (dRaw !== undefined ? (i || '0') + '.' + d : i)
|
|
|
+ }
|
|
|
+ // 边界:只有 - 或 +
|
|
|
+ if (v === '-' || v === '+') {
|
|
|
+ row[prop] = ''
|
|
|
+ return
|
|
|
+ }
|
|
|
+ row[prop] = v
|
|
|
+ },
|
|
|
+ onNumberKeypressNumeric(e, row, column) {
|
|
|
+ const decLimit = Number(column.decimalLength)
|
|
|
+ const ch = e.key
|
|
|
+ if (ch.length !== 1) return
|
|
|
+ // 允许数字
|
|
|
+ if (/[0-9]/.test(ch)) return
|
|
|
+ // 允许一个小数点且仅当 decLimit>0
|
|
|
+ if (ch === '.') {
|
|
|
+ if (isNaN(decLimit) || decLimit <= 0) {
|
|
|
+ e.preventDefault()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const val = String(row[column.prop] || '')
|
|
|
+ if (val.includes('.')) {
|
|
|
+ e.preventDefault()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 允许首位正负号
|
|
|
+ if (ch === '-' || ch === '+') {
|
|
|
+ const val = String(row[column.prop] || '')
|
|
|
+ if (val.length > 0) {
|
|
|
+ e.preventDefault()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 其它全部拦截
|
|
|
+ e.preventDefault()
|
|
|
+ },
|
|
|
+ onNumberPasteNumeric(e, row, column) {
|
|
|
+ const text = (e.clipboardData && e.clipboardData.getData('text')) || ''
|
|
|
+ if (!text) return
|
|
|
+ let v = text.replace(/[^0-9+\-.]/g, '')
|
|
|
+ const decLimit = Number(column.decimalLength)
|
|
|
+ // 一个正负号,且在最前
|
|
|
+ v = v.replace(/(?!^)[+\-]/g, '')
|
|
|
+ // 一个小数点;若整数限定,去掉小数点
|
|
|
+ if (!isNaN(decLimit) && decLimit === 0) {
|
|
|
+ v = v.replace(/[.]/g, '')
|
|
|
+ } else {
|
|
|
+ const parts = v.split('.')
|
|
|
+ if (parts.length > 2) v = parts[0] + '.' + parts.slice(1).join('')
|
|
|
+ }
|
|
|
+ // 应用 input 过滤
|
|
|
+ const prop = column && column.prop
|
|
|
+ if (!prop) return
|
|
|
+ const curr = String(row[prop] == null ? '' : row[prop])
|
|
|
+ const selection =
|
|
|
+ window.getSelection && window.getSelection().toString()
|
|
|
+ // 简化:直接赋值后再走 onNumberInput 进行长度裁剪
|
|
|
+ row[prop] = v
|
|
|
+ this.sanitizeNumberInput(row, column)
|
|
|
+ },
|
|
|
+ onNumberKeydownNumeric(e, row, column) {
|
|
|
+ // 允许的控制键:Backspace, Delete, Tab, Arrow keys, Home, End
|
|
|
+ const code = e.key
|
|
|
+ const allowedControl = [
|
|
|
+ 'Backspace',
|
|
|
+ 'Delete',
|
|
|
+ 'Tab',
|
|
|
+ 'ArrowLeft',
|
|
|
+ 'ArrowRight',
|
|
|
+ 'Home',
|
|
|
+ 'End',
|
|
|
+ ]
|
|
|
+ if (allowedControl.includes(code)) return
|
|
|
+ // 其它按键交给 keypress 处理
|
|
|
+ },
|
|
|
+ onNumberCompositionEndNumeric(row, column) {
|
|
|
+ // 输入法结束后再次清洗
|
|
|
+ this.sanitizeNumberInput(row, column)
|
|
|
+ },
|
|
|
+ computeNumberMaxlength(column) {
|
|
|
+ const intLimit = Number(column.totalLength)
|
|
|
+ const decLimit = Number(column.decimalLength)
|
|
|
+ if (!isNaN(intLimit) && intLimit > 0) {
|
|
|
+ // + 整数位 + 小数点 + 小数位
|
|
|
+ const extra = !isNaN(decLimit) && decLimit > 0 ? 1 + decLimit : 0
|
|
|
+ // 预留一个符号位
|
|
|
+ return intLimit + extra + 1
|
|
|
+ }
|
|
|
+ return undefined
|
|
|
+ },
|
|
|
// 将传入的子项标记为只读(不可编辑/删除),新增的行不受影响
|
|
|
markInitialChildrenReadonly() {
|
|
|
const data = this.fixedAssetsData || []
|
|
|
@@ -1290,74 +1545,140 @@
|
|
|
return false
|
|
|
}
|
|
|
},
|
|
|
- // 保存前验证
|
|
|
+ // 保存前验证(基于 columnsMeta 动态字段规则)
|
|
|
validateBeforeSave() {
|
|
|
- const errors = []
|
|
|
- const categories = Array.isArray(this.fixedAssetsData)
|
|
|
- ? this.fixedAssetsData
|
|
|
- : []
|
|
|
+ // 如果没有字段元数据,使用旧的兜底校验(保持兼容)
|
|
|
+ const metaArr = Array.isArray(this.columnsMeta) ? this.columnsMeta : []
|
|
|
+ if (!metaArr.length) {
|
|
|
+ const fallbackErrors = []
|
|
|
+ this.validationErrors = fallbackErrors
|
|
|
+ return fallbackErrors
|
|
|
+ }
|
|
|
|
|
|
- categories.forEach((category) => {
|
|
|
- const items = Array.isArray(category.items) ? category.items : []
|
|
|
- items.forEach((item, index) => {
|
|
|
- const rowNum = index + 1
|
|
|
- // 必填校验
|
|
|
- if (!item.name) {
|
|
|
- errors.push(
|
|
|
- `${category.category} 第${rowNum}行:项目名称不能为空`
|
|
|
- )
|
|
|
- }
|
|
|
- if (!item.unit) {
|
|
|
- errors.push(
|
|
|
- `${category.category} 第${rowNum}行:计量单位不能为空`
|
|
|
- )
|
|
|
- }
|
|
|
- if (
|
|
|
- item.originalValue === '' ||
|
|
|
- item.originalValue === undefined ||
|
|
|
- item.originalValue === null
|
|
|
- ) {
|
|
|
- errors.push(
|
|
|
- `${category.category} 第${rowNum}行:固定资产原值不能为空`
|
|
|
- )
|
|
|
- } else if (isNaN(Number(item.originalValue))) {
|
|
|
- errors.push(
|
|
|
- `${category.category} 第${rowNum}行:固定资产原值必须为数字`
|
|
|
- )
|
|
|
- }
|
|
|
- if (!item.entryDate) {
|
|
|
- errors.push(
|
|
|
- `${category.category} 第${rowNum}行:入帐或竣工验收日期不能为空`
|
|
|
- )
|
|
|
- }
|
|
|
- if (
|
|
|
- item.depreciationPeriod === '' ||
|
|
|
- item.depreciationPeriod === undefined ||
|
|
|
- item.depreciationPeriod === null
|
|
|
- ) {
|
|
|
- errors.push(
|
|
|
- `${category.category} 第${rowNum}行:折旧年限不能为空`
|
|
|
- )
|
|
|
- } else if (isNaN(Number(item.depreciationPeriod))) {
|
|
|
- errors.push(
|
|
|
- `${category.category} 第${rowNum}行:折旧年限必须为数字`
|
|
|
- )
|
|
|
+ const metaByLabel = new Map()
|
|
|
+ const metaByFieldId = new Map()
|
|
|
+ metaArr.forEach((m) => {
|
|
|
+ if (!m) return
|
|
|
+ if (m.label) metaByLabel.set(String(m.label), m)
|
|
|
+ if (m.fieldId) metaByFieldId.set(String(m.fieldId), m)
|
|
|
+ })
|
|
|
+
|
|
|
+ const getMetaForProp = (prop, column) => {
|
|
|
+ // column.label 优先,其次 fieldId
|
|
|
+ if (column && column.label && metaByLabel.has(String(column.label))) {
|
|
|
+ return metaByLabel.get(String(column.label))
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ column &&
|
|
|
+ column.fieldId &&
|
|
|
+ metaByFieldId.has(String(column.fieldId))
|
|
|
+ ) {
|
|
|
+ return metaByFieldId.get(String(column.fieldId))
|
|
|
+ }
|
|
|
+ // 最后尝试用 prop 当作 label 匹配
|
|
|
+ if (prop && metaByLabel.has(String(prop)))
|
|
|
+ return metaByLabel.get(String(prop))
|
|
|
+ return null
|
|
|
+ }
|
|
|
+
|
|
|
+ const isEmpty = (v) => v === undefined || v === null || v === ''
|
|
|
+ const isValidDate = (v) =>
|
|
|
+ typeof v === 'string' &&
|
|
|
+ /^\d{4}-\d{2}-\d{2}(?:\s+\d{2}:\d{2}(:\d{2})?)?$/.test(v)
|
|
|
+ const isIntegerWithLen = (v, len) => {
|
|
|
+ if (typeof v !== 'string') v = v == null ? '' : String(v)
|
|
|
+ if (!/^[-+]?\d+$/.test(v)) return false
|
|
|
+ const digits = v.replace(/^[-+]?/, '')
|
|
|
+ return len === undefined ? true : digits.length <= len
|
|
|
+ }
|
|
|
+ const isDecimalWithLen = (v, intLen, decLen) => {
|
|
|
+ if (typeof v !== 'string') v = v == null ? '' : String(v)
|
|
|
+ if (!/^[-+]?\d*(?:\.\d+)?$/.test(v)) return false
|
|
|
+ const [intPart, decPart = ''] = v.replace(/^[-+]?/, '').split('.')
|
|
|
+ const intOk = intLen === undefined ? true : intPart.length <= intLen
|
|
|
+ const decOk = decLen === undefined ? true : decPart.length <= decLen
|
|
|
+ // 至少有整数或小数位
|
|
|
+ return intOk && decOk && (intPart.length > 0 || decPart.length > 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ const errors = []
|
|
|
+ const flat = Array.isArray(this.flattenedData) ? this.flattenedData : []
|
|
|
+ flat.forEach((row, rowIndex) => {
|
|
|
+ // 跳过分类行
|
|
|
+ if (
|
|
|
+ row.parentid === '-1' ||
|
|
|
+ row.parentid === -1 ||
|
|
|
+ row.parentId === '-1' ||
|
|
|
+ row.parentId === -1 ||
|
|
|
+ row.isCategory
|
|
|
+ ) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.dynamicColumns.forEach((col) => {
|
|
|
+ const prop = col.prop
|
|
|
+ const value = row[prop]
|
|
|
+ const meta = getMetaForProp(prop, col)
|
|
|
+ if (!meta) return
|
|
|
+
|
|
|
+ // 必填
|
|
|
+ if (meta.required && isEmpty(value)) {
|
|
|
+ errors.push(`第${rowIndex + 1}行【${col.label}】不能为空`)
|
|
|
+ return
|
|
|
}
|
|
|
+ if (isEmpty(value)) return
|
|
|
+
|
|
|
+ const t = String(meta.type || meta.fieldType || '').toLowerCase()
|
|
|
+ const intLen = meta.totalLength
|
|
|
+ const decLen = meta.decimalLength
|
|
|
if (
|
|
|
- item.depreciationExpense === '' ||
|
|
|
- item.depreciationExpense === undefined ||
|
|
|
- item.depreciationExpense === null
|
|
|
+ t === 'number' ||
|
|
|
+ t === 'int' ||
|
|
|
+ t === 'integer' ||
|
|
|
+ t === 'decimal' ||
|
|
|
+ t === 'float' ||
|
|
|
+ t === 'double'
|
|
|
) {
|
|
|
- errors.push(`${category.category} 第${rowNum}行:折旧费不能为空`)
|
|
|
- } else if (isNaN(Number(item.depreciationExpense))) {
|
|
|
- errors.push(
|
|
|
- `${category.category} 第${rowNum}行:折旧费必须为数字`
|
|
|
- )
|
|
|
+ if (!decLen || decLen === 0) {
|
|
|
+ if (!isIntegerWithLen(value, intLen)) {
|
|
|
+ errors.push(
|
|
|
+ `第${rowIndex + 1}行【${col.label}】必须为整数且不超过${
|
|
|
+ intLen || '限定'
|
|
|
+ }位`
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (!isDecimalWithLen(value, intLen, decLen)) {
|
|
|
+ errors.push(
|
|
|
+ `第${rowIndex + 1}行【${col.label}】必须为数字,整数不超过${
|
|
|
+ intLen || '限定'
|
|
|
+ }位,小数不超过${decLen}位`
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (t === 'date' || t === 'datetime' || t === 'year') {
|
|
|
+ if (!isValidDate(value)) {
|
|
|
+ errors.push(
|
|
|
+ `第${rowIndex + 1}行【${
|
|
|
+ col.label
|
|
|
+ }】日期格式不正确,应为YYYY-MM-DD或YYYY-MM-DD HH:mm:ss`
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
- if (!item.fundSource) {
|
|
|
- errors.push(
|
|
|
- `${category.category} 第${rowNum}行:资金来源不能为空`
|
|
|
+
|
|
|
+ // 字典/枚举
|
|
|
+ if (Array.isArray(meta.options) && meta.options.length > 0) {
|
|
|
+ const allowed = new Set(
|
|
|
+ meta.options.map(
|
|
|
+ (o) =>
|
|
|
+ o && (o.value != null ? String(o.value) : String(o.label))
|
|
|
+ )
|
|
|
)
|
|
|
+ const valStr = String(value)
|
|
|
+ if (!allowed.has(valStr)) {
|
|
|
+ errors.push(
|
|
|
+ `第${rowIndex + 1}行【${col.label}】不在允许的取值范围内`
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
})
|
|
|
})
|