增加 3d 展示区块

This commit is contained in:
eafonyang
2026-08-17 14:58:02 +08:00
parent 160307087d
commit 952b8bb76e
6 changed files with 293 additions and 5 deletions
@@ -51,7 +51,8 @@ const layoutOptions: LayoutOption[] = [
{ label: '标题+媒体', value: 'title_media', desc: '顶部标题文案 + 下方图片/视频,对齐方向可配置' },
{ label: '马赛克拼贴', value: 'mosaic', desc: '一大带多小的不规则网格,每个槽位支持图片/视频、中英双素材' },
{ label: '跑马灯', value: 'marquee', desc: '图片/视频轮播,可自定义切换间隔,最多 5 个素材' },
{ label: '参数规格表', value: 'spec_table', desc: '产品技术参数规格表格' }
{ label: '参数规格表', value: 'spec_table', desc: '产品技术参数规格表格' },
{ label: '3D 模型展示', value: 'model3d', desc: 'three.js 交互式 3D 模型(素材库上传 .glb),支持拖拽旋转/缩放/自动旋转' }
];
function layoutLabel(value: string) {
@@ -366,7 +367,7 @@ function handleAdd() {
formData.media_url_en = '';
formData.video_url = '';
formData.is_visible = true;
formData.config = { text_position: 'middle_left', width_percent: 100, container_width: '1200', border_radius: 0, padding_y: null, margin_y: null, title_font_size: null, bg_fit: 'cover', media_position: 'left', split_ratio: 50, mosaic_pattern: 'big_3', mosaic_items: emptyMosaicItems(4), marquee_items: emptyMarqueeItems(1), marquee_interval: 3, min_height: null };
formData.config = { text_position: 'middle_left', width_percent: 100, container_width: '1200', border_radius: 0, padding_y: null, margin_y: null, title_font_size: null, bg_fit: 'cover', media_position: 'left', split_ratio: 50, mosaic_pattern: 'big_3', mosaic_items: emptyMosaicItems(4), marquee_items: emptyMarqueeItems(1), marquee_interval: 3, min_height: null, model_url: '', model_height: 480, auto_rotate: true };
dialogVisible.value = true;
}
@@ -412,7 +413,10 @@ function fillFormFromRow(row: Api.Www.SectionBlock) {
split_ratio: [30, 40, 50, 60, 70].includes(Number(cfg.split_ratio)) ? Number(cfg.split_ratio) : 50,
min_height: typeof cfg.min_height === 'number' && cfg.min_height >= 0 ? cfg.min_height : null,
mosaic_pattern: ['big_3', 'half_2', 'big_4', 'big_2'].includes(String(cfg.mosaic_pattern)) ? String(cfg.mosaic_pattern) : 'big_3',
marquee_interval: typeof cfg.marquee_interval === 'number' && cfg.marquee_interval >= 1 ? Math.min(cfg.marquee_interval, 60) : 3
marquee_interval: typeof cfg.marquee_interval === 'number' && cfg.marquee_interval >= 1 ? Math.min(cfg.marquee_interval, 60) : 3,
model_url: typeof cfg.model_url === 'string' ? cfg.model_url : '',
model_height: typeof cfg.model_height === 'number' && cfg.model_height >= 200 ? cfg.model_height : 480,
auto_rotate: cfg.auto_rotate !== false
};
formData.config.mosaic_items = normalizeMosaicItems(cfg.mosaic_items, String(formData.config.mosaic_pattern));
formData.config.marquee_items = normalizeMarqueeItems(cfg.marquee_items);
@@ -835,6 +839,28 @@ onMounted(loadData);
<NButton :disabled="marqueeItems.length >= 5" @click="handleAddMarqueeItem">+ 添加素材{{ marqueeItems.length }}/5</NButton>
</NFormItem>
</template>
<template v-else-if="formData.layout === 'model3d'">
<NFormItem label="3D 模型">
<MediaSelectInput v-model:value="(formData.config.model_url as string)" file-type="file" :maxlength="500" placeholder="模型文件(素材库上传 .glb/.gltf,建议 .glb" />
</NFormItem>
<div class="grid grid-cols-2 gap-x-16px">
<NFormItem label="展示区高度" label-placement="top">
<NInputNumber
:value="(formData.config.model_height as number) ?? 480"
:min="200"
:max="1200"
:step="20"
class="w-full"
@update:value="v => (formData.config.model_height = v ?? 480)"
>
<template #suffix>px</template>
</NInputNumber>
</NFormItem>
<NFormItem label="自动旋转" label-placement="top">
<NSwitch :value="(formData.config.auto_rotate as boolean) !== false" @update:value="v => (formData.config.auto_rotate = v)" />
</NFormItem>
</div>
</template>
<template v-else>
<NFormItem label="图片(中)">
<MediaSelectInput v-model:value="formData.media_url_zh" file-type="image" :maxlength="500" placeholder="素材路径(支持 /uploads/... 相对路径)" />