// 模块数据驱动列表的「查询条件」工具:构建查询 SQL 片段、解析查询输入/默认值。 // 供 fms-module-list / fms-module-edit 等按模块元数据渲染的组件共用; // 展示格式化(parseOptions / formatDateValue)见 fieldUtils.js。 export const identifierRe = /^[A-Za-z_][A-Za-z0-9_]*$/ export function escapeSqlText(value) { return String(value).replaceAll("'", "''") } export function requireModuleId(value) { const id = String(value ?? '') if (!/^\d+$/.test(id)) throw new Error('模块 ID 格式不正确') return id } export function isEmptyValue(value) { return value === undefined || value === null || value === '' } export function isRangeQuery(item) { return ( item.config.b_operator === 'between' || item.config.b_component === 'number-range' || item.field.b_type === 'datetime' ) } export function isNumericField(item) { return item.field.b_type === 'number' || item.field.b_type === 'money' } export function isDateQuery(item) { return ( item.config.b_component === 'date-picker' || item.field.b_type === 'date' || item.field.b_type === 'datetime' ) } export function emptyQueryValue(item) { if (item.field.b_type === 'checkbox') return '' if (isRangeQuery(item) || item.config.b_operator === 'in') return [] return undefined } export function defaultQueryValue(item) { const value = item.config.b_default_value if (value === null || value === undefined || value === '') return emptyQueryValue(item) if (isRangeQuery(item) || item.config.b_operator === 'in') { try { const parsed = JSON.parse(value) if (Array.isArray(parsed)) return parsed } catch { // 非 JSON 默认值继续按逗号分隔处理。 } return String(value) .split(',') .map((part) => part.trim()) .filter(Boolean) } if (isNumericField(item)) { const number = Number(value) return Number.isFinite(number) ? number : undefined } return value } export function sqlLiteral(value, field, endOfRange = false) { if (isEmptyValue(value)) return null if (field.b_type === 'number' || field.b_type === 'money') { const number = Number(value) return Number.isFinite(number) ? String(number) : null } if (field.b_type === 'checkbox') { return value === true || Number(value) === 1 ? '1' : '0' } let normalized = String(value) const isDate = field.b_type === 'date' || field.b_type === 'datetime' if (isDate && value && typeof value.format === 'function') { normalized = value.format('YYYY-MM-DD') } else if (isDate) { normalized = String(value).slice(0, 10) } if (field.b_type === 'datetime') { normalized += endOfRange ? ' 23:59:59' : ' 00:00:00' } return `N'${escapeSqlText(normalized)}'` } export function buildQueryCondition(item, value) { const fieldName = item.field.b_field if (!identifierRe.test(fieldName)) return '' const field = `[${fieldName}]` if (isRangeQuery(item)) { const values = Array.isArray(value) ? value : [] const start = sqlLiteral(values[0], item.field) const end = sqlLiteral(values[1], item.field, true) if (start && end) return `${field} BETWEEN ${start} AND ${end}` if (start) return `${field} >= ${start}` if (end) return `${field} <= ${end}` return '' } if (item.config.b_operator === 'in') { let values = value if (!Array.isArray(values)) { values = String(value ?? '') .split(',') .map((part) => part.trim()) .filter(Boolean) } const literals = [] for (const entry of values) { const literal = sqlLiteral(entry, item.field) if (literal) literals.push(literal) } return literals.length ? `${field} IN (${literals.join(', ')})` : '' } const literal = sqlLiteral(value, item.field) if (!literal) return '' if (item.config.b_operator === 'like') { return `${field} LIKE N'%${escapeSqlText(String(value))}%'` } return `${field} = ${literal}` }