From 3738bf04c24ea46d880bd510aad4db91e2ad15be Mon Sep 17 00:00:00 2001 From: chen Date: Sat, 19 Sep 2026 16:22:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96(=E8=A3=85=E4=BF=AE):=20?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E9=87=91=E5=88=9A=E5=8C=BA=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E4=B8=BA320x320=E8=B6=85=E6=B8=85=E5=A4=A7=E5=AD=97=E7=89=88?= =?UTF-8?q?=EF=BC=8C=E7=BB=84=E4=BB=B6=E6=94=BE=E5=A4=A7=E8=87=B3120rpx?= =?UTF-8?q?=E5=B9=B6=E7=A0=B4=E9=99=A4=E5=BE=AE=E4=BF=A1=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../decorate-update-partner-centers-2026.sql | 4 +- scripts/generate_banners.py | 39 ++++---- scripts/generate_hd_icons.py | 97 +++++++++++++++++++ scripts/update_remote_db_icons_v2.php | 85 ++++++++++++++++ tests/verify_decorate_partner_centers_spec.js | 13 ++- .../s-menu-button/s-menu-button.vue | 13 ++- 6 files changed, 227 insertions(+), 24 deletions(-) create mode 100644 scripts/generate_hd_icons.py create mode 100644 scripts/update_remote_db_icons_v2.php diff --git a/docs/sql/decorate-update-partner-centers-2026.sql b/docs/sql/decorate-update-partner-centers-2026.sql index 15983d0..9e27a45 100644 --- a/docs/sql/decorate-update-partner-centers-2026.sql +++ b/docs/sql/decorate-update-partner-centers-2026.sql @@ -25,8 +25,10 @@ ON DUPLICATE KEY UPDATE -- 2. 装修模板更新说明: -- 装修模板数据以 JSON 存储在 `hla_shopro_decorate_page`.`page` 字段中。 -- 因徽章图标内部已包含业务名称文字,首页金刚区 menuButton 统一配置为: --- - layout: 2 (纯图标优雅模式,避免下方文字折行重合) +-- - layout: 2 (纯图标优雅模式,无冗余文字) +-- - iconSize: 120 (图标显示尺寸从 98rpx 放大至 120rpx,视觉更饱满大气) -- - title.text: "" (双重保障清空下方文字) +-- - src: 统一升级为 320x320 Retina 视网膜高清且文字放大的 _v2.png 文件,击穿微信本地图片缓存 -- - 各项跳转 url 保留 title 参数,实现点击进入后导航栏仍正确显示各中心标题 -- 备份文件存放于服务器: -- /www/wwwroot/henglian.gynhc.com/backups/decorate_page_backup_20260919.json diff --git a/scripts/generate_banners.py b/scripts/generate_banners.py index 641db9c..f33a4d7 100644 --- a/scripts/generate_banners.py +++ b/scripts/generate_banners.py @@ -291,65 +291,68 @@ build_menu_icons() def build_partner_centers_menu_icons(): import os - font_menu = ImageFont.truetype(font_title, 42) - font_sub = ImageFont.truetype(font_text, 22) - font_sub_3 = ImageFont.truetype(font_text, 19) + font_menu = ImageFont.truetype(font_title, 96) + font_sub = ImageFont.truetype(font_text, 48) + font_sub_3 = ImageFont.truetype(font_text, 42) # 5个清新渐变配置: (文件名, t1, t2, c_top, c_bottom, is_3) items = [ - ('menu_city_center.png', '市级', '运营', (72, 168, 252), (32, 122, 235), False), - ('menu_city_partner.png', '市级', '合伙', (252, 120, 100), (232, 68, 72), False), - ('menu_province_center.png', '省级', '服务', (62, 198, 146), (28, 162, 112), False), - ('menu_province_partner.png', '省级', '合伙', (168, 118, 230), (132, 78, 208), False), - ('menu_region_branch.png', '大区', '分公司', (248, 182, 72), (222, 138, 38), True), + ('menu_city_center.png', '市级', '运营', (68, 165, 255), (28, 118, 240), False), + ('menu_city_partner.png', '市级', '合伙', (255, 115, 96), (235, 60, 68), False), + ('menu_province_center.png', '省级', '服务', (58, 202, 148), (22, 160, 108), False), + ('menu_province_partner.png', '省级', '合伙', (172, 118, 238), (128, 72, 210), False), + ('menu_region_branch.png', '大区', '分公司', (252, 180, 68), (225, 132, 30), True), ] out_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../public/uploads/decorate')) os.makedirs(out_dir, exist_ok=True) for fn, t1, t2, c1, c2, is_3 in items: - size = 160 + size = 320 base = Image.new('RGBA', (size, size), (0, 0, 0, 0)) # 渐变 grad = Image.new('RGBA', (size, size)) for y in range(size): - r = y / size + r = y / float(size) cr = int(c1[0] * (1 - r) + c2[0] * r) cg = int(c1[1] * (1 - r) + c2[1] * r) cb = int(c1[2] * (1 - r) + c2[2] * r) for x in range(size): grad.putpixel((x, y), (cr, cg, cb, 255)) + pad = 12 mask = Image.new('L', (size, size), 0) m_draw = ImageDraw.Draw(mask) - m_draw.ellipse([6, 6, 154, 154], fill=255) + m_draw.ellipse([pad, pad, size - pad, size - pad], fill=255) base.paste(grad, (0, 0), mask) d = ImageDraw.Draw(base) # 高级细双环与微高光 - d.ellipse([6, 6, 154, 154], outline=(255, 255, 255, 220), width=3) - d.ellipse([14, 14, 146, 146], outline=(255, 255, 255, 110), width=1) + d.ellipse([pad, pad, size - pad, size - pad], outline=(255, 255, 255, 230), width=6) + d.ellipse([pad + 16, pad + 16, size - pad - 16, size - pad - 16], outline=(255, 255, 255, 110), width=2) # 顶部轻微晨光弧线 - d.arc([16, 16, 144, 144], start=210, end=330, fill=(255, 255, 255, 140), width=2) + d.arc([pad + 20, pad + 20, size - pad - 20, size - pad - 20], start=210, end=330, fill=(255, 255, 255, 150), width=4) # 标题 1 bbox1 = d.textbbox((0, 0), t1, font=font_menu) w1 = bbox1[2] - bbox1[0] - d.text(((size - w1) / 2, 38), t1, font=font_menu, fill=(255, 255, 255, 255)) + d.text(((size - w1) / 2.0, 66), t1, font=font_menu, fill=(255, 255, 255, 255)) # 标题 2 cur_sub_font = font_sub_3 if is_3 else font_sub bbox2 = d.textbbox((0, 0), t2, font=cur_sub_font) w2 = bbox2[2] - bbox2[0] - y2 = 98 if is_3 else 96 - d.text(((size - w2) / 2, y2), t2, font=cur_sub_font, fill=(255, 255, 255, 240)) + y2 = 192 if is_3 else 188 + d.text(((size - w2) / 2.0, y2), t2, font=cur_sub_font, fill=(255, 255, 255, 245)) save_path = os.path.join(out_dir, fn) + v2_path = os.path.join(out_dir, fn.replace('.png', '_v2.png')) base.save(save_path) - print("Fresh icon generated at", save_path) + base.save(v2_path) + print("Fresh icon generated at", save_path, "and", v2_path) build_partner_centers_menu_icons() diff --git a/scripts/generate_hd_icons.py b/scripts/generate_hd_icons.py new file mode 100644 index 0000000..31a64c7 --- /dev/null +++ b/scripts/generate_hd_icons.py @@ -0,0 +1,97 @@ +import os +from PIL import Image, ImageDraw, ImageFont, ImageFilter + +font_song = "/System/Library/Fonts/Supplemental/Songti.ttc" +font_hei = "/System/Library/Fonts/STHeiti Medium.ttc" +font_hei_bold = "/System/Library/Fonts/Supplemental/Arial Unicode.ttf" + +# 清新渐变配色方案 +items = [ + ('menu_city_center', '市级', '运营', (68, 165, 255), (28, 118, 240), False), + ('menu_city_partner', '市级', '合伙', (255, 115, 96), (235, 60, 68), False), + ('menu_province_center', '省级', '服务', (58, 202, 148), (22, 160, 108), False), + ('menu_province_partner', '省级', '合伙', (172, 118, 238), (128, 72, 210), False), + ('menu_region_branch', '大区', '分公司', (252, 180, 68), (225, 132, 30), True), +] + +def generate_icons(size=320, font_family="song"): + f_main_path = font_song if font_family == "song" else font_hei + f_sub_path = font_hei + + font_main = ImageFont.truetype(f_main_path, 96) + font_sub = ImageFont.truetype(f_sub_path, 48) + font_sub_3 = ImageFont.truetype(f_sub_path, 42) + + out_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../public/uploads/decorate')) + os.makedirs(out_dir, exist_ok=True) + + generated_images = [] + + for base_name, t1, t2, c1, c2, is_3 in items: + # 画布尺寸 + img = Image.new('RGBA', (size, size), (0, 0, 0, 0)) + + # 1. 垂直柔和渐变底色 + grad = Image.new('RGBA', (size, size)) + for y in range(size): + r = y / float(size) + cr = int(c1[0] * (1 - r) + c2[0] * r) + cg = int(c1[1] * (1 - r) + c2[1] * r) + cb = int(c1[2] * (1 - r) + c2[2] * r) + for x in range(size): + grad.putpixel((x, y), (cr, cg, cb, 255)) + + # 2. 圆形遮罩(留 12px 边距防抗锯齿边缘裁切) + pad = 12 + mask = Image.new('L', (size, size), 0) + m_draw = ImageDraw.Draw(mask) + m_draw.ellipse([pad, pad, size - pad, size - pad], fill=255) + img.paste(grad, (0, 0), mask) + + d = ImageDraw.Draw(img) + + # 3. 外层高光细环 + 内层细光环 + d.ellipse([pad, pad, size - pad, size - pad], outline=(255, 255, 255, 230), width=6) + d.ellipse([pad + 16, pad + 16, size - pad - 16, size - pad - 16], outline=(255, 255, 255, 110), width=2) + + # 4. 顶部晨光弧光 + d.arc([pad + 20, pad + 20, size - pad - 20, size - pad - 20], start=210, end=330, fill=(255, 255, 255, 150), width=4) + + # 5. 标题 1 居中绘制 (大字 96px) + bbox1 = d.textbbox((0, 0), t1, font=font_main) + w1 = bbox1[2] - bbox1[0] + h1 = bbox1[3] - bbox1[1] + y1 = 66 + d.text(((size - w1) / 2.0, y1), t1, font=font_main, fill=(255, 255, 255, 255)) + + # 6. 标题 2 居中绘制 (大字 48px / 42px) + cur_sub_font = font_sub_3 if is_3 else font_sub + bbox2 = d.textbbox((0, 0), t2, font=cur_sub_font) + w2 = bbox2[2] - bbox2[0] + y2 = 192 if is_3 else 188 + d.text(((size - w2) / 2.0, y2), t2, font=cur_sub_font, fill=(255, 255, 255, 245)) + + # 保存原文件名及 v2 版本文件名 + p_base = os.path.join(out_dir, f"{base_name}.png") + p_v2 = os.path.join(out_dir, f"{base_name}_v2.png") + img.save(p_base) + img.save(p_v2) + generated_images.append((base_name, img)) + print(f"Generated {p_base} and {p_v2} (size: {size}x{size})") + + # 生成一条 5 个横排在一起的整体预览图 (5 * 320 = 1600 宽) + strip_w = size * len(items) + 80 + strip_h = size + 60 + strip = Image.new('RGBA', (strip_w, strip_h), (246, 248, 250, 255)) + s_draw = ImageDraw.Draw(strip) + for idx, (name, icon) in enumerate(generated_images): + pos_x = 40 + idx * size + pos_y = 30 + strip.paste(icon, (pos_x, pos_y), icon) + + strip_path = os.path.join(out_dir, "fresh_hd_strip_preview.png") + strip.save(strip_path) + print(f"Preview strip saved to {strip_path}") + +if __name__ == '__main__': + generate_icons(320, "song") diff --git a/scripts/update_remote_db_icons_v2.php b/scripts/update_remote_db_icons_v2.php new file mode 100644 index 0000000..1d0f81c --- /dev/null +++ b/scripts/update_remote_db_icons_v2.php @@ -0,0 +1,85 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC + ]); +} catch (Exception $e) { + die("DB connection failed: " . $e->getMessage() . "\n"); +} + +$targetPages = [8, 2]; + +foreach ($targetPages as $pageId) { + $stmt = $pdo->prepare("SELECT id, page FROM hla_shopro_decorate_page WHERE id = ?"); + $stmt->execute([$pageId]); + $row = $stmt->fetch(); + if (!$row) { + echo "Page $pageId not found.\n"; + continue; + } + + $pageData = json_decode($row['page'], true); + if (!is_array($pageData) || !isset($pageData['data']) || !is_array($pageData['data'])) { + echo "Invalid structure in page $pageId.\n"; + continue; + } + + $updated = false; + foreach ($pageData['data'] as &$comp) { + if (isset($comp['type']) && $comp['type'] === 'menuButton') { + echo "Found menuButton in page $pageId.\n"; + $comp['data']['layout'] = 2; // 纯图标模式 + $comp['data']['iconSize'] = 120; // 放大图标至 120rpx + + // 确保 list 中的每个项均指向 _v2.png 高清大图,并清空 title.text + $listConfig = [ + ['src' => '/uploads/decorate/menu_city_center_v2.png', 'url' => '/pages/public/richtext?id=5&title=%E5%B8%82%E7%BA%A7%E8%BF%90%E8%90%A5%E4%B8%AD%E5%BF%83'], + ['src' => '/uploads/decorate/menu_city_partner_v2.png', 'url' => '/pages/public/richtext?id=5&title=%E5%B8%82%E7%BA%A7%E5%90%88%E4%BC%99%E4%BA%BA'], + ['src' => '/uploads/decorate/menu_province_center_v2.png', 'url' => '/pages/public/richtext?id=5&title=%E7%9C%81%E7%BA%A7%E6%9C%8D%E5%8A%A1%E4%B8%AD%E5%BF%83'], + ['src' => '/uploads/decorate/menu_province_partner_v2.png', 'url' => '/pages/public/richtext?id=5&title=%E7%9C%81%E7%BA%A7%E5%90%88%E4%BC%99%E4%BA%BA'], + ['src' => '/uploads/decorate/menu_region_branch_v2.png', 'url' => '/pages/public/richtext?id=5&title=%E5%A4%A7%E5%8C%BA%E5%88%86%E5%85%AC%E5%8F%B8'], + ]; + + if (isset($comp['data']['list']) && is_array($comp['data']['list'])) { + foreach ($comp['data']['list'] as $idx => &$item) { + if (isset($listConfig[$idx])) { + $item['src'] = $listConfig[$idx]['src']; + $item['url'] = $listConfig[$idx]['url']; + $item['title']['text'] = ''; + } + } + } + $updated = true; + } + } + + if ($updated) { + $newJson = json_encode($pageData, JSON_UNESCAPED_UNICODE); + $updateStmt = $pdo->prepare("UPDATE hla_shopro_decorate_page SET page = ? WHERE id = ?"); + $updateStmt->execute([$newJson, $pageId]); + echo "Page $pageId successfully updated to HD v2 icons & iconSize: 120!\n"; + } +} + +// 刷新 Redis 缓存(如果有) +try { + $redis = new Redis(); + if ($redis->connect('127.0.0.1', 6379)) { + // 清理商城相关的装修缓存 + $keys = $redis->keys('*shopro*decorate*'); + if (!empty($keys)) { + $redis->del($keys); + echo "Cleared " . count($keys) . " decorate redis cache keys.\n"; + } + } +} catch (Exception $e) { + echo "Redis notice: " . $e->getMessage() . "\n"; +} + +echo "All Done.\n"; diff --git a/tests/verify_decorate_partner_centers_spec.js b/tests/verify_decorate_partner_centers_spec.js index f43911f..1a4beec 100644 --- a/tests/verify_decorate_partner_centers_spec.js +++ b/tests/verify_decorate_partner_centers_spec.js @@ -23,10 +23,15 @@ console.log("========================================================\n"); console.log("一、静态资源完整性验证:"); const expectedIcons = [ 'menu_city_center.png', + 'menu_city_center_v2.png', 'menu_city_partner.png', + 'menu_city_partner_v2.png', 'menu_province_center.png', + 'menu_province_center_v2.png', 'menu_province_partner.png', - 'menu_region_branch.png' + 'menu_province_partner_v2.png', + 'menu_region_branch.png', + 'menu_region_branch_v2.png' ]; expectedIcons.forEach(iconName => { @@ -34,7 +39,7 @@ expectedIcons.forEach(iconName => { assert(fs.existsSync(iconPath), `图标文件存在: ${iconName}`); if (fs.existsSync(iconPath)) { const stats = fs.statSync(iconPath); - assert(stats.size > 1000, `图标大小正常 (${stats.size} 字节): ${iconName}`); + assert(stats.size > 10000, `图标大小正常(超高清 320x320,${stats.size} 字节): ${iconName}`); } }); @@ -112,10 +117,12 @@ curl -s -k --resolve henglian.gynhc.com:443:127.0.0.1 'https://henglian.gynhc.co assert(list.length === 5, `正常场景: menuButton 包含 5 个金刚区项目(实际 ${list.length} 个)`); assert(menuBtn?.data?.layout === 2, '正常场景: menuButton 采用 layout=2 纯图标模式'); + assert(menuBtn?.data?.iconSize === 120, '正常场景: menuButton 配置了 iconSize=120rpx 高清大尺寸'); assert(list.every(item => item.title?.text === ''), '正常场景: 金刚区下方文本已全部清空,避免折行重叠'); + assert(list.every(item => item.src && item.src.includes('_v2.png')), '正常场景: 图标全部升级为 _v2.png 320x320 超清大字版,彻底击穿客户端强缓存'); const expectedTitles = ['市级运营中心', '市级合伙人', '省级服务中心', '省级合伙人', '大区分公司']; - const allUrlsCorrect = list.every((item, idx) => item.url === `/pages/public/richtext?id=5&title=${expectedTitles[idx]}`); + const allUrlsCorrect = list.every((item, idx) => item.url === `/pages/public/richtext?id=5&title=${encodeURIComponent(expectedTitles[idx])}`); assert(allUrlsCorrect, '正常场景: 5 个项目的跳转链接均正确绑定富文本待开发页及对应标题'); // 场景 2:正常场景 - 富文本待开发数据接口 diff --git a/uniapp/sheep/components/s-menu-button/s-menu-button.vue b/uniapp/sheep/components/s-menu-button/s-menu-button.vue index dc865c2..62b3b24 100644 --- a/uniapp/sheep/components/s-menu-button/s-menu-button.vue +++ b/uniapp/sheep/components/s-menu-button/s-menu-button.vue @@ -41,10 +41,11 @@ splitData(props.data.list, props.data.row * props.data.col)); const swiperHeight = computed(() => props.data.row * (props.data.layout == 1 ? 200 : 160)); + const getIconSize = computed(() => { + if (props.data && props.data.iconSize) return props.data.iconSize; + return props.data && props.data.layout == 2 ? 120 : props.iconSize; + }); const windowWidth = sheep.$platform.device.windowWidth; const clWidth = computed( () => @@ -273,6 +278,10 @@ width: 98rpx; height: 98rpx; padding-bottom: 10rpx; + + &.menu-icon-pure { + padding-bottom: 0; + } } .menu-title {