diff --git a/scripts/copy-h5-nav-assets.cjs b/scripts/copy-h5-nav-assets.cjs index 58b076e..f8c5dc4 100644 --- a/scripts/copy-h5-nav-assets.cjs +++ b/scripts/copy-h5-nav-assets.cjs @@ -70,6 +70,8 @@ const copyStaticFile = (fileName) => { copyStaticSubtree('nav-assets') copyStaticSubtree('guide-data') copyStaticSubtree('icons') +// 讲解业务单元封面由 H5 运行时按 /static/explain/... 动态加载。 +copyStaticSubtree('explain') copyStaticSubtree('sgs-map-sdk') copyStaticSubtree('three') copyStaticFile('exhibit-placeholder.jpg') diff --git a/scripts/generate-explain-unit-previews.cjs b/scripts/generate-explain-unit-previews.cjs new file mode 100644 index 0000000..b4b5aca --- /dev/null +++ b/scripts/generate-explain-unit-previews.cjs @@ -0,0 +1,173 @@ +const fs = require('fs') +const path = require('path') + +const projectRoot = path.resolve(__dirname, '..') +const outputDir = path.join(projectRoot, 'static', 'explain', 'unit-previews') + +// 业务单元来自当前 H5 讲解页实际渲染的一级 outline,生成结果需与映射表同步。 +const units = [ + { id: '7467940240901013508', hall: '地球厅', name: '第一单元:宜居地球', theme: 'earth' }, + { id: '7467940240901013509', hall: '地球厅', name: '第二单元:地史探源', theme: 'geology' }, + { id: '7467940240901013510', hall: '地球厅', name: '第三单元:华夏山水', theme: 'landscape' }, + { id: '7467940240901013511', hall: '地球厅', name: '第四单元:矿产资源——大地的馈赠', theme: 'mineral' }, + { id: '7467940240901013512', hall: '地球厅', name: '专题区:地心穿越+自然灾害', theme: 'disaster' }, + { id: '7467940240901013530', hall: '家园厅', name: '序厅', theme: 'home' }, + { id: '7467940240901013531', hall: '家园厅', name: '第一单元:天地共鉴', theme: 'home' }, + { id: '7467940240901013532', hall: '家园厅', name: '第二单元:万物共生', theme: 'ecology' }, + { id: '7467940240901013533', hall: '家园厅', name: '第三单元:家园共筑', theme: 'home' }, + { id: '7467940240901013534', hall: '家园厅', name: '尾厅', theme: 'home' }, + { id: '7467940240901013513', hall: '恐龙厅', name: '第一单元:初识恐龙', theme: 'dinosaur' }, + { id: '7467940240901013514', hall: '恐龙厅', name: '第二单元:恐龙解密', theme: 'dinosaur' }, + { id: '7467940240901013515', hall: '恐龙厅', name: '第三单元:恐龙时代', theme: 'dinosaur' }, + { id: '7467940240901013516', hall: '恐龙厅', name: '第四单元:粤赣育龙', theme: 'dinosaur' }, + { id: '7467940240901013517', hall: '恐龙厅', name: '第五单元:终结与新生', theme: 'dinosaur' }, + { id: '2060939176503275521', hall: '人类厅', name: '第一单元:什么是灵长类', theme: 'human' }, + { id: '2060939176637493249', hall: '人类厅', name: '第二单元:猿与猴', theme: 'human' }, + { id: '2060939176767516674', hall: '人类厅', name: '第三单元:丛林远足', theme: 'forest' }, + { id: '2060939176897540098', hall: '人类厅', name: '第四单元:与巨兽同行', theme: 'human' }, + { id: '2060939177027563521', hall: '人类厅', name: '第五单元:从燧石到硅基', theme: 'human' }, + { id: '7467940240901013523', hall: '生态厅', name: '前言', theme: 'ecology' }, + { id: '7467940240901013524', hall: '生态厅', name: '第二单元:各得其所——世界动物地理区系', theme: 'ecology' }, + { id: '7467940240901013525', hall: '生态厅', name: '第三单元:地球之窗——全球主要生态系统景观', theme: 'ecology' }, + { id: '7467940240901013526', hall: '生物厅', name: '序篇:生物的命名与分类', theme: 'biology' }, + { id: '7467940240901013527', hall: '生物厅', name: '第二单元:中国南方保护动物', theme: 'biology' }, + { id: '7467940240901013528', hall: '生物厅', name: '第三单元:生物基础知识', theme: 'biology' }, + { id: '7467940240901013529', hall: '生物厅', name: '第四单元:生物大观', theme: 'biology' }, + { id: '7467940240901013518', hall: '演化厅', name: '第零单元:基于博物学的进化思想', theme: 'evolution' }, + { id: '7467940240901013519', hall: '演化厅', name: '第一单元:经典进化学说——影响世界的理论', theme: 'evolution' }, + { id: '7467940240901013520', hall: '演化厅', name: '第二单元:遗传与变异——演化的内在驱动力', theme: 'evolution' }, + { id: '7467940240901013521', hall: '演化厅', name: '第三单元:演化方向的选择——适应', theme: 'evolution' }, + { id: '7467940240901013522', hall: '演化厅', name: '第四单元:三十八亿年的创造——从单细胞到智慧生命', theme: 'evolution' }, + { id: '7467940240901013505', hall: '宇宙厅', name: '第一单元:仰望苍穹——人类对宇宙认识的历程', theme: 'cosmos' }, + { id: '7467940240901013506', hall: '宇宙厅', name: '第二单元:拾级而观——等级宇宙结构', theme: 'cosmos' }, + { id: '7467940240901013507', hall: '宇宙厅', name: '第三单元:采石知天——行星科学与深空探测', theme: 'meteorite' } +] + +const themes = { + earth: ['#d7e9d2', '#fbf6d8', '#2f6f5f', '#8eac50'], + geology: ['#ebe1d3', '#bfd5dc', '#674c3b', '#c08c45'], + landscape: ['#dcefd7', '#f4efd0', '#265d4e', '#92b65a'], + mineral: ['#e8e1d4', '#f1f4df', '#4b4f5a', '#c2a95b'], + disaster: ['#eee0d1', '#d7e3e0', '#5b3d35', '#d69d45'], + home: ['#edf2df', '#f5f2d7', '#334c45', '#b2bf47'], + ecology: ['#dbeedb', '#f4f2d1', '#2a5a49', '#87a946'], + dinosaur: ['#e7e0cd', '#dce8d5', '#4d4932', '#9a7844'], + human: ['#e8e0d7', '#eef0d6', '#4b3f36', '#b8884b'], + forest: ['#d9eddc', '#eff3d2', '#2f5f43', '#76a159'], + biology: ['#dff0df', '#eef4d4', '#2f604b', '#7bb36c'], + evolution: ['#e6e6d8', '#dbe9df', '#3f5147', '#9ea34a'], + cosmos: ['#dce5ef', '#f2f1d4', '#26354e', '#7b8bb7'], + meteorite: ['#dedfe5', '#f4efd5', '#343746', '#b19b56'], + fallback: ['#e8eadf', '#f7f5df', '#3f4d43', '#a8b64c'] +} + +const escapeXml = (value) => String(value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + +const shortTitle = (name) => name + .replace(/^第[一二三四五六七八九十零]+单元[::]?/, '') + .replace(/^专题区[::]?/, '') + .replace(/^序篇[::]?/, '') + .replace(/[——+].*$/, '') + .trim() + +const motif = (theme, accent, dark) => { + if (theme === 'cosmos') { + return ` + + + + + ` + } + if (theme === 'meteorite' || theme === 'mineral' || theme === 'geology') { + return ` + + + + ` + } + if (theme === 'dinosaur') { + return ` + + + + + ` + } + if (theme === 'human') { + return ` + + + + ` + } + if (theme === 'biology' || theme === 'ecology' || theme === 'forest') { + return ` + + + + + ` + } + if (theme === 'evolution') { + return ` + + + + + + ` + } + return ` + + + + ` +} + +const svgFor = ({ id, hall, name, theme }) => { + const [start, end, dark, accent] = themes[theme] || themes.fallback + const title = shortTitle(name) || name + + return ` + + + + + + + + + + + + + + ${motif(theme, accent, dark)} + + + ${escapeXml(hall)} + ${escapeXml(title.slice(0, 11))} + 自然导览 · 图文讲解 + + + +` +} + +fs.mkdirSync(outputDir, { recursive: true }) +units.forEach((unit) => { + fs.writeFileSync(path.join(outputDir, `${unit.id}.svg`), svgFor(unit), 'utf8') +}) +fs.writeFileSync(path.join(outputDir, 'default.svg'), svgFor({ + id: 'default', + hall: '讲解', + name: '自然博物馆导览', + theme: 'fallback' +}), 'utf8') + +console.log(`Generated ${units.length + 1} explain unit preview SVG files in ${path.relative(projectRoot, outputDir)}`) diff --git a/src/components/explain/ExplainHallSelect.vue b/src/components/explain/ExplainHallSelect.vue index 0e60936..7568b06 100644 --- a/src/components/explain/ExplainHallSelect.vue +++ b/src/components/explain/ExplainHallSelect.vue @@ -62,9 +62,12 @@ class="hall-card unit-card" @tap="handleBusinessUnitClick(unit.id)" > - - {{ unit.name.slice(0, 1) || '讲' }} - + {{ unit.name }} @@ -122,7 +125,10 @@