优化(装修): 升级金刚区图标为320x320超清大字版,组件放大至120rpx并破除微信缓存
这个提交包含在:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
97
scripts/generate_hd_icons.py
普通文件
97
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")
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
// 更新线上数据库装修配置为 320x320 高清大字版图标,并设置 iconSize 为 120
|
||||
$dsn = 'mysql:host=127.0.0.1;dbname=henglianshutong;charset=utf8mb4';
|
||||
$user = 'henglianshutong';
|
||||
$pass = 'henglianshutong';
|
||||
|
||||
try {
|
||||
$pdo = new PDO($dsn, $user, $pass, [
|
||||
PDO::ATTR_ERRMODE => 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";
|
||||
@@ -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:正常场景 - 富文本待开发数据接口
|
||||
|
||||
@@ -41,10 +41,11 @@
|
||||
<image
|
||||
v-if="item.src"
|
||||
class="menu-icon"
|
||||
:class="{ 'menu-icon-pure': data.layout == 2 }"
|
||||
:style="[
|
||||
{
|
||||
width: props.iconSize + 'rpx',
|
||||
height: props.iconSize + 'rpx',
|
||||
width: getIconSize + 'rpx',
|
||||
height: getIconSize + 'rpx',
|
||||
},
|
||||
]"
|
||||
:src="sheep.$url.cdn(item.src)"
|
||||
@@ -208,6 +209,10 @@
|
||||
// 生成数据
|
||||
const menuList = computed(() => 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 {
|
||||
|
||||
在新工单中引用
屏蔽一个用户