flnxi-Wu/lib/field-data-storage.ts
WuYongJie 63ec6b7dfb fix: 修复排行榜按钮消失、tag排序和输入框浅色模式问题
1. 排行榜悬浮窗关闭后小按钮消失问题
   - 修复面板居中模式关闭时按钮位置错误
   - 保存并恢复按钮的居中状态

2. 首页tag按钮按素材数量排序
   - 添加按素材数量降序排序逻辑

3. 上传弹窗输入框浅色模式文字不可见
   - 添加 text-slate-900 确保浅色模式下文字可见
   - 添加 placeholder 样式确保占位符正确显示
2025-12-14 21:41:29 +08:00

262 lines
6.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 自定义字段数据存储和读取工具
*
* 功能:
* - 将自定义字段key映射到Material模型的标准字段
* - 非标准字段存储到metadata JSON字段
* - 读取时合并标准字段和metadata数据
*/
/**
* Material模型的标准字段列表
* 这些字段直接存储在数据库列中
*/
export const STANDARD_MATERIAL_FIELDS = [
'productModel',
'brandName',
'seriesName',
'skuCode',
'materialTexture',
'categoryName',
'sceneName',
'materialType',
'materialKind',
'renderer',
'renderTime',
'imageResolution',
'colorMode',
'authorName',
'platformName',
] as const
export type StandardMaterialField = typeof STANDARD_MATERIAL_FIELDS[number]
/**
* 检查字段key是否为标准字段
*/
export function isStandardField(fieldKey: string): fieldKey is StandardMaterialField {
return STANDARD_MATERIAL_FIELDS.includes(fieldKey as StandardMaterialField)
}
/**
* 字段值类型
*/
export type FieldValue = string | number | boolean | null | undefined
/**
* 分离标准字段和自定义字段
*/
export function separateFieldValues(
templateFields: Record<string, FieldValue>
): {
standardFields: Partial<Record<StandardMaterialField, string>>
customFields: Record<string, FieldValue>
} {
const standardFields: Partial<Record<StandardMaterialField, string>> = {}
const customFields: Record<string, FieldValue> = {}
for (const [key, value] of Object.entries(templateFields)) {
if (isStandardField(key)) {
// 标准字段转换为字符串存储
standardFields[key] = value === null || value === undefined ? '' : String(value)
} else {
// 自定义字段保留原始类型
customFields[key] = value
}
}
return { standardFields, customFields }
}
/**
* 构建metadata JSON字符串
*/
export function buildMetadata(
customFields: Record<string, FieldValue>,
templateId?: string | null,
previewUrls?: string[],
existingMetadata?: string | null
): string | null {
// 解析现有metadata
let metadataObj: Record<string, unknown> = {}
if (existingMetadata) {
try {
metadataObj = JSON.parse(existingMetadata)
} catch {
// 忽略解析错误
}
}
// 更新templateId
if (templateId) {
metadataObj.templateId = templateId
}
// 更新自定义字段
if (Object.keys(customFields).length > 0) {
metadataObj.templateFields = {
...(metadataObj.templateFields as Record<string, unknown> || {}),
...customFields,
}
}
// 更新预览图URLs
if (previewUrls && previewUrls.length > 0) {
metadataObj.previewUrls = previewUrls
}
// 如果没有任何数据,返回null
if (Object.keys(metadataObj).length === 0) {
return null
}
return JSON.stringify(metadataObj)
}
/**
* 解析metadata JSON字符串
*/
export function parseMetadata(metadata: string | null | undefined): {
templateId?: string
templateFields: Record<string, FieldValue>
previewUrls?: string[]
} {
if (!metadata) {
return { templateFields: {} }
}
try {
const parsed = JSON.parse(metadata)
return {
templateId: parsed.templateId,
templateFields: parsed.templateFields || {},
previewUrls: parsed.previewUrls,
}
} catch {
return { templateFields: {} }
}
}
/**
* 从Material记录中读取所有字段值(合并标准字段和metadata)
*/
export function readAllFieldValues(
material: Record<string, unknown>
): Record<string, FieldValue> {
const result: Record<string, FieldValue> = {}
// 读取标准字段
for (const field of STANDARD_MATERIAL_FIELDS) {
const value = material[field]
if (value !== undefined && value !== null && value !== '') {
result[field] = value as FieldValue
}
}
// 读取metadata中的自定义字段
const metadata = parseMetadata(material.metadata as string | null)
for (const [key, value] of Object.entries(metadata.templateFields)) {
// 自定义字段不覆盖标准字段
if (!isStandardField(key)) {
result[key] = value
}
}
return result
}
/**
* 准备Material创建/更新数据
*/
export function prepareMaterialData(
templateFields: Record<string, FieldValue>,
templateId?: string | null,
previewUrls?: string[],
existingMetadata?: string | null
): {
standardData: Partial<Record<StandardMaterialField, string>>
metadata: string | null
} {
const { standardFields, customFields } = separateFieldValues(templateFields)
const metadata = buildMetadata(customFields, templateId, previewUrls, existingMetadata)
return {
standardData: standardFields,
metadata,
}
}
/**
* 检查metadata大小是否超过限制
*/
export function checkMetadataSize(
metadata: string | null,
maxSizeBytes: number = 65535 // 默认64KB,适合TEXT类型
): { ok: boolean; size: number; maxSize: number } {
const size = metadata ? new TextEncoder().encode(metadata).length : 0
return {
ok: size <= maxSizeBytes,
size,
maxSize: maxSizeBytes,
}
}
/**
* 压缩metadata(移除空值和默认值)
*/
export function compressMetadata(metadata: string | null): string | null {
if (!metadata) return null
try {
const parsed = JSON.parse(metadata)
// 移除空的templateFields
if (parsed.templateFields) {
const cleanedFields: Record<string, unknown> = {}
for (const [key, value] of Object.entries(parsed.templateFields)) {
if (value !== null && value !== undefined && value !== '') {
cleanedFields[key] = value
}
}
if (Object.keys(cleanedFields).length > 0) {
parsed.templateFields = cleanedFields
} else {
delete parsed.templateFields
}
}
// 移除空的previewUrls
if (parsed.previewUrls && Array.isArray(parsed.previewUrls) && parsed.previewUrls.length === 0) {
delete parsed.previewUrls
}
if (Object.keys(parsed).length === 0) {
return null
}
return JSON.stringify(parsed)
} catch {
return metadata
}
}
/**
* 获取字段的存储位置
*/
export function getFieldStorageLocation(fieldKey: string): 'standard' | 'metadata' {
return isStandardField(fieldKey) ? 'standard' : 'metadata'
}
/**
* 批量获取字段存储位置
*/
export function getFieldsStorageLocations(
fieldKeys: string[]
): Map<string, 'standard' | 'metadata'> {
const result = new Map<string, 'standard' | 'metadata'>()
for (const key of fieldKeys) {
result.set(key, getFieldStorageLocation(key))
}
return result
}