diff --git a/furniture-3d-system/prisma/schema.prisma b/furniture-3d-system/prisma/schema.prisma index 955bf4be..2c8ad3cc 100644 --- a/furniture-3d-system/prisma/schema.prisma +++ b/furniture-3d-system/prisma/schema.prisma @@ -446,8 +446,8 @@ model Substrate { thicknessSpec SubstrateThicknessSpec @relation(fields: [thicknessSpecId], references: [id]) surfaceOptionId String? surfaceOption SubstrateSurfaceOption? @relation(fields: [surfaceOptionId], references: [id]) - systemNameOptionId String - systemNameOption SubstrateSystemNameOption @relation(fields: [systemNameOptionId], references: [id]) + systemNameOptionId String? + systemNameOption SubstrateSystemNameOption? @relation(fields: [systemNameOptionId], references: [id]) suggestedPrice Decimal? @db.Decimal(10, 2) weightKg Float? metadata Json? diff --git a/furniture-3d-system/src/app/api/asset-center/substrate-dictionaries/[id]/route.ts b/furniture-3d-system/src/app/api/asset-center/substrate-dictionaries/[id]/route.ts index 1f52afc8..6dc0219c 100644 --- a/furniture-3d-system/src/app/api/asset-center/substrate-dictionaries/[id]/route.ts +++ b/furniture-3d-system/src/app/api/asset-center/substrate-dictionaries/[id]/route.ts @@ -49,8 +49,7 @@ export async function DELETE(request: NextRequest, context: RouteParams) { type !== 'CATEGORY' && type !== 'ENVIRONMENT_GRADE' && type !== 'THICKNESS_SPEC' && - type !== 'SURFACE_OPTION' && - type !== 'SYSTEM_NAME_OPTION' + type !== 'SURFACE_OPTION' ) { return jsonError(ErrorCode.VALIDATION_FAILED, '缺少有效的字典类型', 400, ctx.requestId) } diff --git a/furniture-3d-system/src/app/api/platform/schemas.ts b/furniture-3d-system/src/app/api/platform/schemas.ts index f9165cac..65889adb 100644 --- a/furniture-3d-system/src/app/api/platform/schemas.ts +++ b/furniture-3d-system/src/app/api/platform/schemas.ts @@ -104,7 +104,6 @@ export const substrateCreateSchema = z.object({ environmentGradeId: z.string().trim().min(1), thicknessSpecId: z.string().trim().min(1), surfaceOptionId: z.string().trim().min(1).optional().nullable(), - systemNameOptionId: z.string().trim().min(1), suggestedPrice: z.number().nonnegative().optional().nullable(), weightKg: z.number().nonnegative().optional().nullable(), finishIds: z.array(z.string().trim().min(1)).optional(), @@ -118,7 +117,6 @@ export const substrateUpdateSchema = z.object({ environmentGradeId: z.string().trim().min(1).optional(), thicknessSpecId: z.string().trim().min(1).optional(), surfaceOptionId: z.string().trim().min(1).optional().nullable(), - systemNameOptionId: z.string().trim().min(1).optional(), suggestedPrice: z.number().nonnegative().optional().nullable(), weightKg: z.number().nonnegative().optional().nullable(), finishIds: z.array(z.string().trim().min(1)).optional(), @@ -131,7 +129,6 @@ export const substrateDictionaryTypeSchema = z.enum([ 'ENVIRONMENT_GRADE', 'THICKNESS_SPEC', 'SURFACE_OPTION', - 'SYSTEM_NAME_OPTION', ]) export const substrateDictionaryCreateSchema = z.object({ diff --git a/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.test.tsx b/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.test.tsx index f7e6cc8d..057a8924 100644 --- a/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.test.tsx +++ b/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.test.tsx @@ -54,7 +54,6 @@ const dictionaries: SubstrateDictionaryBundleDTO = { environmentGrades: [{ id: 'grade-e1', type: 'ENVIRONMENT_GRADE', name: 'E1', sortOrder: 0, isActive: true, createdAt: new Date(), updatedAt: new Date() }], thicknessSpecs: [{ id: 'th-18', type: 'THICKNESS_SPEC', name: '18', sortOrder: 0, isActive: true, thicknessMm: 18, sheetLengthMm: 2440, sheetWidthMm: 1220, createdAt: new Date(), updatedAt: new Date() }], surfaceOptions: [{ id: 'surface-double', type: 'SURFACE_OPTION', name: '双贴', sortOrder: 0, isActive: true, createdAt: new Date(), updatedAt: new Date() }], - systemNameOptions: [{ id: 'sys-18e1', type: 'SYSTEM_NAME_OPTION', name: '18E1级刨花板', sortOrder: 0, isActive: true, createdAt: new Date(), updatedAt: new Date() }], } function renderClient() { @@ -76,8 +75,6 @@ function renderClient() { sheetWidthMm: 1220, surfaceOptionId: 'surface-double', surfaceOptionName: '双贴', - systemNameOptionId: 'sys-18e1', - systemNameOptionName: '18E1级刨花板', suggestedPrice: 180, weightKg: null, finishes: [], @@ -118,8 +115,6 @@ describe('MaterialLibraryClient', () => { sheetWidthMm: 1220, surfaceOptionId: 'surface-double', surfaceOptionName: '双贴', - systemNameOptionId: 'sys-18e1', - systemNameOptionName: '18E1级刨花板', suggestedPrice: 200, weightKg: null, finishes: [], @@ -145,7 +140,6 @@ describe('MaterialLibraryClient', () => { environmentGradeId: 'grade-e1', thicknessSpecId: 'th-18', surfaceOptionId: 'surface-double', - systemNameOptionId: 'sys-18e1', suggestedPrice: 200, }), ) diff --git a/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.tsx b/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.tsx index c65555ee..b44d6c59 100644 --- a/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.tsx +++ b/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.tsx @@ -4,6 +4,7 @@ import { startTransition, useMemo, useRef, useState } from 'react' import { ArrowLeft, Check, + ChevronDown, ImagePlus, Package2, Pencil, @@ -72,7 +73,6 @@ type SubstrateFormState = { environmentGradeId: string thicknessSpecId: string surfaceOptionId: string - systemNameOptionId: string suggestedPrice: string weightKg: string finishIds: string[] @@ -144,7 +144,6 @@ function emptySubstrateForm(dict: SubstrateDictionaryBundleDTO): SubstrateFormSt environmentGradeId: dict.environmentGrades[0]?.id ?? '', thicknessSpecId: dict.thicknessSpecs[0]?.id ?? '', surfaceOptionId: dict.surfaceOptions[0]?.id ?? '', - systemNameOptionId: dict.systemNameOptions[0]?.id ?? '', suggestedPrice: '', weightKg: '', finishIds: [], @@ -170,8 +169,6 @@ function dictionaryOptionsByType(bundle: SubstrateDictionaryBundleDTO, type: Sub return bundle.thicknessSpecs case 'SURFACE_OPTION': return bundle.surfaceOptions - case 'SYSTEM_NAME_OPTION': - return bundle.systemNameOptions } } @@ -185,8 +182,6 @@ function dictionaryLabel(type: SubstrateDictionaryType) { return '厚度规格' case 'SURFACE_OPTION': return '贴面类型' - case 'SYSTEM_NAME_OPTION': - return '系统名称' } } @@ -225,7 +220,8 @@ export default function MaterialLibraryClient({ const [substrateForm, setSubstrateForm] = useState( emptySubstrateForm(initialSubstrateDictionaries), ) - const [finishPickerOpen, setFinishPickerOpen] = useState(false) + const [finishSearch, setFinishSearch] = useState('') + const [finishDropdownOpen, setFinishDropdownOpen] = useState(false) // Dictionary management state const [dictionaryModalOpen, setDictionaryModalOpen] = useState(false) @@ -241,7 +237,6 @@ export default function MaterialLibraryClient({ ...dictionaries.environmentGrades, ...dictionaries.thicknessSpecs, ...dictionaries.surfaceOptions, - ...dictionaries.systemNameOptions, ] const map = new Map() for (const item of all) map.set(item.id, item) @@ -255,7 +250,6 @@ export default function MaterialLibraryClient({ ENVIRONMENT_GRADE: new Set(substrates.map((item) => item.environmentGradeId).filter(Boolean)), THICKNESS_SPEC: new Set(substrates.map((item) => item.thicknessSpecId).filter(Boolean)), SURFACE_OPTION: new Set(substrates.map((item) => item.surfaceOptionId).filter(Boolean)), - SYSTEM_NAME_OPTION: new Set(substrates.map((item) => item.systemNameOptionId).filter(Boolean)), } as Record> }, [substrates]) @@ -284,14 +278,20 @@ export default function MaterialLibraryClient({ ) }, [substrates, searchText]) - const isSolidWood = substrateForm.categoryId === 'cat-solid' || substrateForm.categoryText === '实木' + const isSolidWood = (() => { + const catName = dictById.get(substrateForm.categoryId)?.name ?? substrateForm.categoryText + if (!catName) return false + const solidKeywords = ['实木', '橡胶木', '橡木', '黑胡桃', '白蜡', '榉木', '桦木', '松木', '柞木'] + return solidKeywords.some((kw) => catName.includes(kw)) + })() - // Filter finishes by material type + // Filter finishes by material type, exclude 五金油漆/漆面颜色/五金漆面色 (these are for hardware accessories only) const filteredFinishesForSubstrate = useMemo(() => { + const excludeCategories = ['五金油漆', '漆面颜色', '五金漆面色'] if (isSolidWood) { - return finishes.filter((f) => f.categoryName === '实木花色') + return finishes.filter((f) => f.categoryName === '实木花色' && !excludeCategories.includes(f.categoryName ?? '')) } - return finishes.filter((f) => f.categoryName !== '实木花色') + return finishes.filter((f) => f.categoryName !== '实木花色' && !excludeCategories.includes(f.categoryName ?? '')) }, [finishes, isSolidWood]) // Get the default finish category for inline creation @@ -302,20 +302,26 @@ export default function MaterialLibraryClient({ return categories.find((c) => c.name === '木纹贴面花色')?.id ?? categories[0]?.id ?? '' }, [categories, isSolidWood]) + const finishAutoSuggestions = useMemo(() => { + const kw = finishSearch.trim().toLowerCase() + if (!kw) return [] + return filteredFinishesForSubstrate + .filter((f) => f.name.toLowerCase().includes(kw)) + .slice(0, 8) + }, [finishSearch, filteredFinishesForSubstrate]) + const previewSubstrateName = useMemo(() => { - const cat = substrateForm.categoryText.trim() || dictById.get(substrateForm.categoryId)?.name || '' + const cat = dictById.get(substrateForm.categoryId)?.name || '' const env = dictById.get(substrateForm.environmentGradeId)?.name ?? '' const thick = dictionaries.thicknessSpecs.find((t) => t.id === substrateForm.thicknessSpecId) - const surface = isSolidWood ? '' : (dictById.get(substrateForm.surfaceOptionId)?.name ?? '') - const finishNames = substrateForm.finishIds - .map((fid) => finishes.find((f) => f.id === fid)?.name) - .filter(Boolean) as string[] - const newFinish = substrateForm.newFinishName.trim() - if (newFinish) finishNames.push(newFinish) + const surface = dictById.get(substrateForm.surfaceOptionId)?.name ?? '' + const finishName = substrateForm.finishIds.length > 0 + ? finishes.find((f) => f.id === substrateForm.finishIds[0])?.name + : substrateForm.newFinishName.trim() || '' if (!cat || !env || !thick) return '' - const thickness = thick.name.toUpperCase() - const finishPart = finishNames.length > 0 ? finishNames.join(' ') : '' - return [thickness, env, cat, finishPart, surface].filter(Boolean).join(' ') + const thickness = `${thick.thicknessMm ?? thick.name}` + const envPart = env.endsWith('级') ? env : `${env}级` + return [thickness, envPart, cat, finishName, surface].filter(Boolean).join(' ') }, [substrateForm, dictById, dictionaries.thicknessSpecs, finishes, isSolidWood]) // Selected items @@ -371,7 +377,6 @@ export default function MaterialLibraryClient({ environmentGradeId: s.environmentGradeId, thicknessSpecId: s.thicknessSpecId, surfaceOptionId: s.surfaceOptionId ?? '', - systemNameOptionId: s.systemNameOptionId, suggestedPrice: s.suggestedPrice != null ? String(s.suggestedPrice) : '', weightKg: s.weightKg != null ? String(s.weightKg) : '', finishIds: s.finishes.map((f) => f.id), @@ -420,7 +425,7 @@ export default function MaterialLibraryClient({ async function handleSaveSubstrate() { const solidWood = substrateForm.categoryId === 'cat-solid' || substrateForm.categoryText.trim() === '实木' - if (!substrateForm.categoryText.trim() || !substrateForm.environmentGradeId || !substrateForm.thicknessSpecId || !substrateForm.systemNameOptionId) { + if (!substrateForm.categoryText.trim() || !substrateForm.environmentGradeId || !substrateForm.thicknessSpecId) { toast.error('请补齐必填选项'); return } if (!solidWood && !substrateForm.surfaceOptionId) { @@ -470,7 +475,6 @@ export default function MaterialLibraryClient({ environmentGradeId: substrateForm.environmentGradeId, thicknessSpecId: substrateForm.thicknessSpecId, surfaceOptionId: solidWood ? undefined : substrateForm.surfaceOptionId, - systemNameOptionId: substrateForm.systemNameOptionId, suggestedPrice: toNum(substrateForm.suggestedPrice), weightKg: toNum(substrateForm.weightKg), finishIds: finishIds.length > 0 ? finishIds : undefined, @@ -533,11 +537,29 @@ export default function MaterialLibraryClient({ } async function handleSaveDictionary() { - if (!dictionaryForm.name.trim()) { + if (activeDictionaryType === 'THICKNESS_SPEC') { + if (!dictionaryForm.thicknessMm.trim() || !Number.isFinite(Number(dictionaryForm.thicknessMm))) { + toast.error('请填写有效的厚度数值') + return + } + } else if (!dictionaryForm.name.trim()) { toast.error(`请填写${dictionaryLabel(activeDictionaryType)}名称`) return } + // Uniqueness check for non-THICKNESS_SPEC types + if (activeDictionaryType !== 'THICKNESS_SPEC') { + const trimmedName = dictionaryForm.name.trim() + const existingOptions = dictionaryOptionsByType(dictionaries, activeDictionaryType) + const duplicate = existingOptions.find( + (opt) => opt.name === trimmedName && opt.id !== editingDictionaryId, + ) + if (duplicate) { + toast.error(`已存在同名「${trimmedName}」,不可重复创建`) + return + } + } + setSubmitting(true) try { if (dictionaryModalMode === 'create') { @@ -583,7 +605,6 @@ export default function MaterialLibraryClient({ environmentGrades: patch(current.environmentGrades), thicknessSpecs: patch(current.thicknessSpecs), surfaceOptions: patch(current.surfaceOptions), - systemNameOptions: patch(current.systemNameOptions), } }) setDictionaryModalOpen(false) @@ -619,7 +640,6 @@ export default function MaterialLibraryClient({ environmentGrades: current.environmentGrades.filter((item) => item.id !== deleteTarget.id), thicknessSpecs: current.thicknessSpecs.filter((item) => item.id !== deleteTarget.id), surfaceOptions: current.surfaceOptions.filter((item) => item.id !== deleteTarget.id), - systemNameOptions: current.systemNameOptions.filter((item) => item.id !== deleteTarget.id), })) setDeleteTarget(null) toast.success(`${dictionaryLabel(deleteTarget.dictionaryType)}已删除`) @@ -820,122 +840,137 @@ export default function MaterialLibraryClient({ {/* Auto-generated name preview */}
-
-

材质

- { - const text = e.target.value - const match = dictionaries.categories.find((d) => d.name === text) - setSubstrateForm((c) => ({ - ...c, - categoryText: text, - categoryId: match ? match.id : '', - })) - }} - placeholder="选择或输入材质名称" - className="w-full rounded-apple-md border border-apple-separator bg-apple-bg-primary px-3 py-1.5 text-sm text-apple-label-primary placeholder:text-apple-label-tertiary" - /> - - {dictionaries.categories.map((d) => ( - -
+ setSubstrateForm((c) => ({ ...c, environmentGradeId: e.target.value }))} options={dictionaries.environmentGrades.map((d) => ({ value: d.id, label: d.name }))} /> setSubstrateForm((c) => ({ ...c, surfaceOptionId: e.target.value }))} options={dictionaries.surfaceOptions.map((d) => ({ value: d.id, label: d.name }))} />} + setSubstrateForm((c) => ({ ...c, suggestedPrice: e.target.value }))} placeholder="例如 180" /> setSubstrateForm((c) => ({ ...c, weightKg: e.target.value }))} placeholder="例如 32" /> -
- {/* Finish multi-select */} -
-
-

花色

-
- -
-
- {/* Inline new finish input */} -
- setSubstrateForm((c) => ({ ...c, newFinishName: e.target.value }))} - placeholder="输入新花色名称,保存时自动创建" - className="flex-1 rounded-apple-md border border-apple-separator bg-apple-bg-primary px-3 py-1.5 text-sm text-apple-label-primary placeholder:text-apple-label-tertiary" - /> - {substrateForm.newFinishName.trim() && ( - + {/* Finish single-select inside grid */} +
+ +
+
- {/* Pending new finish badge */} - {substrateForm.newFinishName.trim() && ( -
- - + {substrateForm.newFinishName.trim()}(新建) - - -
- )} - {substrateForm.finishIds.length > 0 && ( -
- {substrateForm.finishIds.map((fid) => { - const f = finishes.find((x) => x.id === fid) - if (!f) return null + onClick={() => { setFinishDropdownOpen(true); setFinishSearch('') }} + > + {(() => { + const selectedFinish = substrateForm.finishIds.length > 0 + ? finishes.find((x) => x.id === substrateForm.finishIds[0]) + : null + const pendingNew = substrateForm.newFinishName.trim() + if (pendingNew && !selectedFinish) { return ( - - {f.name} - + + + {pendingNew}(新建) + ) - })} -
- )} - {finishPickerOpen && ( -
- {filteredFinishesForSubstrate.length === 0 ? ( -

暂无花色,请在上方输入框新建

- ) : ( - filteredFinishesForSubstrate.map((f) => { - const selected = substrateForm.finishIds.includes(f.id) - return ( - + + ) + } + return ( + { setFinishSearch(e.target.value); setFinishDropdownOpen(true) }} + onFocus={() => setFinishDropdownOpen(true)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault() + const name = finishSearch.trim() + if (!name) return + const existing = filteredFinishesForSubstrate.find((f) => f.name === name) + if (existing) { + setSubstrateForm((c) => ({ ...c, finishIds: [existing.id], newFinishName: '' })) + } else { + setSubstrateForm((c) => ({ ...c, finishIds: [], newFinishName: name })) + } + setFinishSearch('') + setFinishDropdownOpen(false) + } + }} + placeholder="输入花色名称搜索" + className="flex-1 bg-transparent px-3 py-1.5 text-sm text-apple-label-primary placeholder:text-apple-label-tertiary focus:outline-none" + /> + ) + })()} + +
+ {/* Dropdown */} + {finishDropdownOpen && ( +
+
+ {finishSearch.trim() ? ( + finishAutoSuggestions.length > 0 ? ( + finishAutoSuggestions.map((f) => ( + + )) + ) : ( + ) - }) - )} + ) : ( + filteredFinishesForSubstrate.slice(0, 15).map((f) => { + const selected = substrateForm.finishIds.includes(f.id) + return ( + + ) + }) + )} +
)} +
+
))}
- setDictionaryForm((current) => ({ ...current, name: event.target.value }))} /> - setDictionaryForm((current) => ({ ...current, sortOrder: event.target.value }))} /> - {activeDictionaryType === 'THICKNESS_SPEC' ? setDictionaryForm((current) => ({ ...current, thicknessMm: event.target.value }))} /> : null} + {activeDictionaryType === 'THICKNESS_SPEC' ? ( + <> + setDictionaryForm((current) => ({ ...current, thicknessMm: event.target.value, name: event.target.value }))} placeholder="例如 12、15、18" /> + setDictionaryForm((current) => ({ ...current, sortOrder: event.target.value }))} /> + + ) : ( + <> + setDictionaryForm((current) => ({ ...current, name: event.target.value }))} /> + setDictionaryForm((current) => ({ ...current, sortOrder: event.target.value }))} /> + + )}
diff --git a/furniture-3d-system/src/lib/open-product/substrate-rules.ts b/furniture-3d-system/src/lib/open-product/substrate-rules.ts index 6b4aa793..7436ff70 100644 --- a/furniture-3d-system/src/lib/open-product/substrate-rules.ts +++ b/furniture-3d-system/src/lib/open-product/substrate-rules.ts @@ -15,17 +15,23 @@ function toFinitePositive(value: number | null | undefined): number | null { } export function buildSubstrateDisplayName(params: { - thicknessLabel: string + thicknessMm: number environmentGradeLabel: string - systemNameLabel: string + categoryLabel: string + finishLabels?: string[] + surfaceLabel?: string }): string { + const envLabel = params.environmentGradeLabel.trim() + const envWithSuffix = envLabel ? (envLabel.endsWith('级') ? envLabel : `${envLabel}级`) : '' return [ - params.thicknessLabel.trim(), - params.environmentGradeLabel.trim(), - params.systemNameLabel.trim(), + `${params.thicknessMm}`, + envWithSuffix, + params.categoryLabel.trim(), + ...(params.finishLabels ?? []), + params.surfaceLabel?.trim() ?? '', ] .filter(Boolean) - .join('') + .join(' ') } export function buildSubstrateSummaryLabel(params: { diff --git a/furniture-3d-system/src/server/repositories/asset-center.repository.ts b/furniture-3d-system/src/server/repositories/asset-center.repository.ts index 72b14562..0a509ce0 100644 --- a/furniture-3d-system/src/server/repositories/asset-center.repository.ts +++ b/furniture-3d-system/src/server/repositories/asset-center.repository.ts @@ -40,7 +40,6 @@ export interface SubstrateWithRelations extends Substrate { environmentGrade: { id: string; name: string } thicknessSpec: { id: string; name: string; thicknessMm: number; sheetLengthMm: number; sheetWidthMm: number } surfaceOption: { id: string; name: string } | null - systemNameOption: { id: string; name: string } finishes: { finish: { id: string @@ -89,7 +88,6 @@ const substrateRelationInclude = { }, }, surfaceOption: { select: { id: true, name: true } }, - systemNameOption: { select: { id: true, name: true } }, finishes: { select: { finish: { diff --git a/furniture-3d-system/src/server/services/__tests__/asset-center.substrate.service.test.ts b/furniture-3d-system/src/server/services/__tests__/asset-center.substrate.service.test.ts index d5d0c4bc..56beff2f 100644 --- a/furniture-3d-system/src/server/services/__tests__/asset-center.substrate.service.test.ts +++ b/furniture-3d-system/src/server/services/__tests__/asset-center.substrate.service.test.ts @@ -103,8 +103,6 @@ describe('assetCenterService.createSubstrate', () => { }, surfaceOptionId: null, surfaceOption: null, - systemNameOptionId: 'sys-solid', - systemNameOption: { id: 'sys-solid', name: '实木板' }, suggestedPrice: 320, weightKg: 32, metadata: null, @@ -124,7 +122,6 @@ describe('assetCenterService.createSubstrate', () => { environmentGradeId: 'grade-e1', thicknessSpecId: 'th-18', surfaceOptionId: null, - systemNameOptionId: 'sys-solid', suggestedPrice: 320, weightKg: 32, finishIds: ['finish-oak', 'finish-walnut'], diff --git a/furniture-3d-system/src/server/services/asset-center.service.ts b/furniture-3d-system/src/server/services/asset-center.service.ts index 01fc6a3e..05289144 100644 --- a/furniture-3d-system/src/server/services/asset-center.service.ts +++ b/furniture-3d-system/src/server/services/asset-center.service.ts @@ -234,8 +234,6 @@ function toSubstrateDTO(record: SubstrateWithRelations): SubstrateDTO { sheetWidthMm: record.thicknessSpec.sheetWidthMm, surfaceOptionId: record.surfaceOptionId, surfaceOptionName: record.surfaceOption?.name ?? null, - systemNameOptionId: record.systemNameOptionId, - systemNameOptionName: record.systemNameOption.name, suggestedPrice: decimalToNumber(record.suggestedPrice), weightKg: record.weightKg ?? null, finishes: (record.finishes ?? []).map((item) => ({ @@ -279,8 +277,6 @@ function getDictionaryLabel(type: SubstrateDictionaryType): string { return '厚度规格' case 'SURFACE_OPTION': return '贴面类型' - case 'SYSTEM_NAME_OPTION': - return '系统名称' } } @@ -349,9 +345,8 @@ async function loadSubstrateParts(input: { environmentGradeId: string thicknessSpecId: string surfaceOptionId: string | null - systemNameOptionId: string }) { - const [category, environmentGrade, thicknessSpec, surfaceOption, systemNameOption] = + const [category, environmentGrade, thicknessSpec, surfaceOption] = await Promise.all([ prisma.substrateCategory.findUnique({ where: { id: input.categoryId } }), prisma.substrateEnvironmentGrade.findUnique({ where: { id: input.environmentGradeId } }), @@ -359,7 +354,6 @@ async function loadSubstrateParts(input: { input.surfaceOptionId ? prisma.substrateSurfaceOption.findUnique({ where: { id: input.surfaceOptionId } }) : Promise.resolve(null), - prisma.substrateSystemNameOption.findUnique({ where: { id: input.systemNameOptionId } }), ]) if (!category) { @@ -374,16 +368,12 @@ async function loadSubstrateParts(input: { if (input.surfaceOptionId && !surfaceOption) { throw new AppError(ErrorCode.SUBSTRATE_NOT_FOUND, '贴面类型不存在') } - if (!systemNameOption) { - throw new AppError(ErrorCode.SUBSTRATE_NOT_FOUND, '系统名称不存在') - } return { category, environmentGrade, thicknessSpec, surfaceOption, - systemNameOption, } } @@ -392,13 +382,12 @@ async function buildSubstratePayload(input: { environmentGradeId: string thicknessSpecId: string surfaceOptionId: string | null - systemNameOptionId: string }) { const parts = await loadSubstrateParts(input) const generatedName = buildSubstrateDisplayName({ - thicknessLabel: parts.thicknessSpec.name, + thicknessMm: parts.thicknessSpec.thicknessMm, environmentGradeLabel: parts.environmentGrade.name, - systemNameLabel: parts.systemNameOption.name, + categoryLabel: parts.category.name, }) return { @@ -438,7 +427,6 @@ async function buildSubstratePayloadWithFinishes(input: { environmentGradeId: string thicknessSpecId: string surfaceOptionId: string | null - systemNameOptionId: string finishIds: string[] }) { const parts = await loadSubstrateParts(input) @@ -452,15 +440,13 @@ async function buildSubstratePayloadWithFinishes(input: { : [] const finishPart = finishRows.map((item) => item.name).join(' ') - const generatedName = [ - parts.thicknessSpec.name.toUpperCase(), - parts.environmentGrade.name, - parts.category.name, - finishPart, - parts.surfaceOption?.name ?? '', - ] - .filter(Boolean) - .join(' ') + const generatedName = buildSubstrateDisplayName({ + thicknessMm: parts.thicknessSpec.thicknessMm, + environmentGradeLabel: parts.environmentGrade.name, + categoryLabel: parts.category.name, + finishLabels: finishPart ? [finishPart] : [], + surfaceLabel: parts.surfaceOption?.name ?? '', + }) return { generatedName, @@ -529,13 +515,12 @@ class AssetCenterService { } async getSubstrateDictionaryBundle(): Promise { - const [categories, environmentGrades, thicknessSpecs, surfaceOptions, systemNameOptions] = + const [categories, environmentGrades, thicknessSpecs, surfaceOptions] = await Promise.all([ prisma.substrateCategory.findMany({ orderBy: [{ sortOrder: 'asc' }, { name: 'asc' }] }), prisma.substrateEnvironmentGrade.findMany({ orderBy: [{ sortOrder: 'asc' }, { name: 'asc' }] }), prisma.substrateThicknessSpec.findMany({ orderBy: [{ sortOrder: 'asc' }, { thicknessMm: 'asc' }] }), prisma.substrateSurfaceOption.findMany({ orderBy: [{ sortOrder: 'asc' }, { name: 'asc' }] }), - prisma.substrateSystemNameOption.findMany({ orderBy: [{ sortOrder: 'asc' }, { name: 'asc' }] }), ]) return { @@ -543,7 +528,6 @@ class AssetCenterService { environmentGrades: environmentGrades.map((item) => toDictionaryOptionDTO('ENVIRONMENT_GRADE', item)), thicknessSpecs: thicknessSpecs.map((item) => toDictionaryOptionDTO('THICKNESS_SPEC', item)), surfaceOptions: surfaceOptions.map((item) => toDictionaryOptionDTO('SURFACE_OPTION', item)), - systemNameOptions: systemNameOptions.map((item) => toDictionaryOptionDTO('SYSTEM_NAME_OPTION', item)), } } @@ -626,7 +610,6 @@ class AssetCenterService { environmentGradeId: input.environmentGradeId, thicknessSpecId: input.thicknessSpecId, surfaceOptionId: input.surfaceOptionId ?? null, - systemNameOptionId: input.systemNameOptionId, isActive: true, }, include: { @@ -650,7 +633,6 @@ class AssetCenterService { environmentGradeId: existing.environmentGradeId, thicknessSpecId: existing.thicknessSpecId, surfaceOptionId: existing.surfaceOptionId ?? null, - systemNameOptionId: existing.systemNameOptionId, finishIds: mergedFinishIds, }) @@ -683,7 +665,6 @@ class AssetCenterService { environmentGradeId: input.environmentGradeId, thicknessSpecId: input.thicknessSpecId, surfaceOptionId: input.surfaceOptionId ?? null, - systemNameOptionId: input.systemNameOptionId, finishIds, }) @@ -698,7 +679,6 @@ class AssetCenterService { surfaceOption: input.surfaceOptionId ? { connect: { id: input.surfaceOptionId } } : undefined, - systemNameOption: { connect: { id: input.systemNameOptionId } }, suggestedPrice: input.suggestedPrice ?? null, weightKg: input.weightKg ?? null, finishes: @@ -720,7 +700,6 @@ class AssetCenterService { select: { id: true, name: true, thicknessMm: true, sheetLengthMm: true, sheetWidthMm: true }, }, surfaceOption: { select: { id: true, name: true } }, - systemNameOption: { select: { id: true, name: true } }, finishes: { include: { finish: { @@ -760,7 +739,6 @@ class AssetCenterService { const nextThicknessSpecId = input.thicknessSpecId ?? existing.thicknessSpecId const nextSurfaceOptionId = input.surfaceOptionId !== undefined ? input.surfaceOptionId : existing.surfaceOptionId - const nextSystemNameOptionId = input.systemNameOptionId ?? existing.systemNameOptionId const nextFinishIds = input.finishIds !== undefined ? normalizeFinishIds(input.finishIds) @@ -774,13 +752,12 @@ class AssetCenterService { environmentGradeId: nextEnvironmentGradeId, thicknessSpecId: nextThicknessSpecId, surfaceOptionId: nextSurfaceOptionId ?? null, - systemNameOptionId: nextSystemNameOptionId, isActive: true, }, select: { id: true }, }) if (duplicate) { - throw new AppError(ErrorCode.VALIDATION_FAILED, '已存在相同 5 字段规格的基材,请直接维护已有基材') + throw new AppError(ErrorCode.VALIDATION_FAILED, '已存在相同规格的基材,请直接维护已有基材') } const payload = await buildSubstratePayloadWithFinishes({ @@ -788,7 +765,6 @@ class AssetCenterService { environmentGradeId: nextEnvironmentGradeId, thicknessSpecId: nextThicknessSpecId, surfaceOptionId: nextSurfaceOptionId ?? null, - systemNameOptionId: nextSystemNameOptionId, finishIds: nextFinishIds, }) @@ -815,10 +791,6 @@ class AssetCenterService { : input.surfaceOptionId ? { connect: { id: input.surfaceOptionId } } : { disconnect: true }, - systemNameOption: - input.systemNameOptionId === undefined - ? undefined - : { connect: { id: input.systemNameOptionId } }, suggestedPrice: input.suggestedPrice === undefined ? undefined : input.suggestedPrice, weightKg: input.weightKg === undefined ? undefined : input.weightKg, finishes: @@ -840,7 +812,6 @@ class AssetCenterService { select: { id: true, name: true, thicknessMm: true, sheetLengthMm: true, sheetWidthMm: true }, }, surfaceOption: { select: { id: true, name: true } }, - systemNameOption: { select: { id: true, name: true } }, finishes: { include: { finish: { @@ -913,11 +884,6 @@ class AssetCenterService { 'SURFACE_OPTION', await prisma.substrateSurfaceOption.create({ data: dataBase }), ) - case 'SYSTEM_NAME_OPTION': - return toDictionaryOptionDTO( - 'SYSTEM_NAME_OPTION', - await prisma.substrateSystemNameOption.create({ data: dataBase }), - ) } } catch (error) { if ( @@ -988,18 +954,6 @@ class AssetCenterService { }, }), ) - case 'SYSTEM_NAME_OPTION': - return toDictionaryOptionDTO( - 'SYSTEM_NAME_OPTION', - await prisma.substrateSystemNameOption.update({ - where: { id }, - data: { - name: input.name?.trim(), - sortOrder: input.sortOrder, - isActive: input.isActive, - }, - }), - ) } } catch (error) { if ( @@ -1024,7 +978,6 @@ class AssetCenterService { ENVIRONMENT_GRADE: { environmentGradeId: id }, THICKNESS_SPEC: { thicknessSpecId: id }, SURFACE_OPTION: { surfaceOptionId: id }, - SYSTEM_NAME_OPTION: { systemNameOptionId: id }, } const referenceCount = await prisma.substrate.count({ where: whereByType[type] }) if (referenceCount > 0) { @@ -1045,9 +998,6 @@ class AssetCenterService { case 'SURFACE_OPTION': await prisma.substrateSurfaceOption.delete({ where: { id } }) return - case 'SYSTEM_NAME_OPTION': - await prisma.substrateSystemNameOption.delete({ where: { id } }) - return } } catch (error) { if ( diff --git a/furniture-3d-system/src/server/services/asset-center.service.types.ts b/furniture-3d-system/src/server/services/asset-center.service.types.ts index 2c88911d..e62684e3 100644 --- a/furniture-3d-system/src/server/services/asset-center.service.types.ts +++ b/furniture-3d-system/src/server/services/asset-center.service.types.ts @@ -100,8 +100,6 @@ export interface SubstrateDTO { sheetWidthMm: number surfaceOptionId: string | null surfaceOptionName: string | null - systemNameOptionId: string - systemNameOptionName: string suggestedPrice: number | null weightKg: number | null finishes: { @@ -121,7 +119,6 @@ export type SubstrateDictionaryType = | 'ENVIRONMENT_GRADE' | 'THICKNESS_SPEC' | 'SURFACE_OPTION' - | 'SYSTEM_NAME_OPTION' export interface SubstrateDictionaryOptionDTO { id: string @@ -141,7 +138,6 @@ export interface SubstrateDictionaryBundleDTO { environmentGrades: SubstrateDictionaryOptionDTO[] thicknessSpecs: SubstrateDictionaryOptionDTO[] surfaceOptions: SubstrateDictionaryOptionDTO[] - systemNameOptions: SubstrateDictionaryOptionDTO[] } export interface CreateAssetFamilyInput { @@ -223,7 +219,6 @@ export interface CreateSubstrateInput { environmentGradeId: string thicknessSpecId: string surfaceOptionId?: string | null - systemNameOptionId: string suggestedPrice?: number | null weightKg?: number | null finishIds?: string[] @@ -238,7 +233,6 @@ export interface UpdateSubstrateInput { environmentGradeId?: string thicknessSpecId?: string surfaceOptionId?: string | null - systemNameOptionId?: string suggestedPrice?: number | null weightKg?: number | null finishIds?: string[] diff --git a/furniture-3d-system/src/server/services/open-platform-demo-seed.service.ts b/furniture-3d-system/src/server/services/open-platform-demo-seed.service.ts index ee66c3e3..a74065dd 100644 --- a/furniture-3d-system/src/server/services/open-platform-demo-seed.service.ts +++ b/furniture-3d-system/src/server/services/open-platform-demo-seed.service.ts @@ -341,7 +341,7 @@ async function ensureSubstrate(creatorId: string) { return existing } - const [category, environmentGrade, thicknessSpec, surfaceOption, systemNameOption] = await Promise.all([ + const [category, environmentGrade, thicknessSpec, surfaceOption] = await Promise.all([ prisma.substrateCategory.upsert({ where: { name: '颗粒板' }, update: {}, @@ -362,11 +362,6 @@ async function ensureSubstrate(creatorId: string) { update: {}, create: { name: '双贴', sortOrder: 0, isActive: true }, }), - prisma.substrateSystemNameOption.upsert({ - where: { name: '18E1级颗粒板' }, - update: {}, - create: { name: '18E1级颗粒板', sortOrder: 0, isActive: true }, - }), ]) return assetCenterService.createSubstrate({ @@ -376,7 +371,6 @@ async function ensureSubstrate(creatorId: string) { environmentGradeId: environmentGrade.id, thicknessSpecId: thicknessSpec.id, surfaceOptionId: surfaceOption.id, - systemNameOptionId: systemNameOption.id, suggestedPrice: 180, metadata: { seedKey: OPEN_PLATFORM_DEMO_SEED_MARKER,