feat(decorate): 首页金刚区替换为各级运营与合伙中心并支持待开发内容展示
|
之后 宽度: | 高度: | 大小: 6.1 KiB |
|
之后 宽度: | 高度: | 大小: 6.3 KiB |
|
之后 宽度: | 高度: | 大小: 7.1 KiB |
二进制
docs/archive_static_backup/menu_gift.png
可执行文件
|
之后 宽度: | 高度: | 大小: 5.6 KiB |
|
之后 宽度: | 高度: | 大小: 5.5 KiB |
|
之后 宽度: | 高度: | 大小: 7.1 KiB |
|
之后 宽度: | 高度: | 大小: 6.8 KiB |
|
之后 宽度: | 高度: | 大小: 5.8 KiB |
|
之后 宽度: | 高度: | 大小: 6.3 KiB |
二进制
docs/archive_static_backup/menu_wine.png
可执行文件
|
之后 宽度: | 高度: | 大小: 5.4 KiB |
@@ -0,0 +1,33 @@
|
||||
-- ==============================================================================
|
||||
-- 恒链数通商城 - 首页金刚区装修调整与服务中心待开发页面配置
|
||||
-- 归档时间: 2026-09-19
|
||||
-- 适用平台: WechatOfficialAccount, WechatMiniProgram, H5, App
|
||||
-- 说明:
|
||||
-- 1. 在 hla_shopro_data_richtext 插入/更新待开发富文本记录(ID: 5)
|
||||
-- 2. 更新装修模板(template_id: 5 / page_id: 8 以及备用模板 template_id: 2 / page_id: 2)
|
||||
-- 将金刚区 menuButton 调整为:市级运营中心、市级合伙人、省级服务中心、省级合伙人、大区分公司
|
||||
-- ==============================================================================
|
||||
|
||||
-- 1. 插入待开发富文本记录 (ID: 5)
|
||||
INSERT INTO `hla_shopro_data_richtext` (`id`, `title`, `content`, `createtime`, `updatetime`)
|
||||
VALUES (
|
||||
5,
|
||||
'待开发,敬请期待',
|
||||
'<div style="padding: 60px 20px 40px; text-align: center; font-family: sans-serif;"><div style="width: 76px; height: 76px; margin: 0 auto 20px; border-radius: 50%; background: #FAF5EB; border: 1px solid #E5D3B3; display: flex; align-items: center; justify-content: center;"><span style="font-size: 34px; line-height: 76px;">⏳</span></div><div style="font-size: 20px; font-weight: bold; color: #262626; margin-bottom: 12px; letter-spacing: 1px;">待开发,敬请期待</div><div style="font-size: 14px; color: #8C8C8C; line-height: 1.6; max-width: 280px; margin: 0 auto;">该业务板块正在加速筹备建设中<br/>更多专属权益即将上线</div></div>',
|
||||
UNIX_TIMESTAMP(),
|
||||
UNIX_TIMESTAMP()
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`title` = VALUES(`title`),
|
||||
`content` = VALUES(`content`),
|
||||
`updatetime` = VALUES(`updatetime`);
|
||||
|
||||
-- 2. 装修模板更新说明:
|
||||
-- 装修模板数据以 JSON 存储在 `hla_shopro_decorate_page`.`page` 字段中。
|
||||
-- 已通过运维安全执行脚本完成原子更新,备份文件存放于服务器:
|
||||
-- /www/wwwroot/henglian.gynhc.com/backups/decorate_page_backup_20260919.json
|
||||
|
||||
-- 回滚说明:
|
||||
-- 若需回滚原金刚区配置,可执行:
|
||||
-- python3 scripts/generate_banners.py 重新生成原图标
|
||||
-- 或读取 backups/decorate_page_backup_20260919.json 恢复 page 字段。
|
||||
@@ -289,6 +289,51 @@ def build_menu_icons():
|
||||
|
||||
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)
|
||||
|
||||
menus = [
|
||||
("menu_city_center.png", "市级", "运营", (25, 45, 80), (230, 200, 130), False),
|
||||
("menu_city_partner.png", "市级", "合伙", (85, 25, 30), (225, 185, 120), False),
|
||||
("menu_province_center.png", "省级", "服务", (20, 55, 45), (145, 215, 180), False),
|
||||
("menu_province_partner.png", "省级", "合伙", (65, 28, 68), (240, 195, 150), False),
|
||||
("menu_region_branch.png", "大区", "分公司", (32, 30, 26), (245, 210, 135), True),
|
||||
]
|
||||
|
||||
out_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../public/uploads/decorate'))
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
|
||||
for filename, t1, t2, bg_c, fg_c, is_3 in menus:
|
||||
size = (160, 160)
|
||||
img = Image.new('RGBA', size, (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
# Soft background circle with border
|
||||
d.ellipse([6, 6, 154, 154], fill=(*bg_c, 245), outline=(*fg_c, 210), width=3)
|
||||
d.ellipse([14, 14, 146, 146], outline=(*fg_c, 85), width=1)
|
||||
|
||||
# Text 1
|
||||
bbox1 = d.textbbox((0, 0), t1, font=font_menu)
|
||||
w1 = bbox1[2] - bbox1[0]
|
||||
x1 = (160 - w1) / 2
|
||||
d.text((x1, 40), t1, font=font_menu, fill=(*fg_c, 255))
|
||||
|
||||
# Text 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]
|
||||
x2 = (160 - w2) / 2
|
||||
y2 = 98 if is_3 else 96
|
||||
d.text((x2, y2), t2, font=cur_sub_font, fill=(245, 245, 245, 230))
|
||||
|
||||
img.save(os.path.join(out_dir, filename))
|
||||
print(filename, "generated at", out_dir)
|
||||
|
||||
build_partner_centers_menu_icons()
|
||||
|
||||
def build_trust_bar():
|
||||
size = (1420, 110)
|
||||
img = Image.new('RGBA', size, (0, 0, 0, 0))
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, testName, detail = '') {
|
||||
if (condition) {
|
||||
console.log(` [PASS] ${testName}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(` [FAIL] ${testName} - ${detail}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("========================================================");
|
||||
console.log(" 首页金刚区装修调整与服务中心待开发页面规范化测试");
|
||||
console.log("========================================================\n");
|
||||
|
||||
// 1. 静态资源完整性验证
|
||||
console.log("一、静态资源完整性验证:");
|
||||
const expectedIcons = [
|
||||
'menu_city_center.png',
|
||||
'menu_city_partner.png',
|
||||
'menu_province_center.png',
|
||||
'menu_province_partner.png',
|
||||
'menu_region_branch.png'
|
||||
];
|
||||
|
||||
expectedIcons.forEach(iconName => {
|
||||
const iconPath = path.resolve(__dirname, '../public/uploads/decorate', iconName);
|
||||
assert(fs.existsSync(iconPath), `图标文件存在: ${iconName}`);
|
||||
if (fs.existsSync(iconPath)) {
|
||||
const stats = fs.statSync(iconPath);
|
||||
assert(stats.size > 1000, `图标大小正常 (${stats.size} 字节): ${iconName}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 生成脚本与前端代码验证
|
||||
console.log("\n二、生成脚本与前端代码优化验证:");
|
||||
const bannerScript = path.resolve(__dirname, '../scripts/generate_banners.py');
|
||||
const bannerContent = fs.readFileSync(bannerScript, 'utf-8');
|
||||
assert(bannerContent.includes('build_partner_centers_menu_icons'), 'generate_banners.py 包含新业务中心徽标生成函数');
|
||||
assert(bannerContent.includes('menu_city_center.png') && bannerContent.includes('menu_region_branch.png'),
|
||||
'generate_banners.py 包含5个新徽标配置定义');
|
||||
|
||||
const menuButtonVue = path.resolve(__dirname, '../uniapp/sheep/components/s-menu-button/s-menu-button.vue');
|
||||
const menuButtonContent = fs.readFileSync(menuButtonVue, 'utf-8');
|
||||
assert(menuButtonContent.includes('font-size: 24rpx;'), 's-menu-button.vue 优化了文字大小为 24rpx,完美适配 5 列 6 字符单行排版');
|
||||
|
||||
const richtextVue = path.resolve(__dirname, '../uniapp/pages/public/richtext.vue');
|
||||
const richtextContent = fs.readFileSync(richtextVue, 'utf-8');
|
||||
assert(richtextContent.includes('defaultPendingContent') && richtextContent.includes('待开发,敬请期待'),
|
||||
'richtext.vue 增加了待开发默认降级与优雅展示兜底');
|
||||
|
||||
const sqlFile = path.resolve(__dirname, '../docs/sql/decorate-update-partner-centers-2026.sql');
|
||||
assert(fs.existsSync(sqlFile), '已归档 SQL 迁移脚本 docs/sql/decorate-update-partner-centers-2026.sql');
|
||||
|
||||
// 3. 业务逻辑与参数模拟测试
|
||||
console.log("\n三、前端业务跳转与标题回显逻辑模拟测试:");
|
||||
const expectedItems = [
|
||||
{ name: '市级运营中心', sub: '运营', file: 'menu_city_center.png' },
|
||||
{ name: '市级合伙人', sub: '合伙', file: 'menu_city_partner.png' },
|
||||
{ name: '省级服务中心', sub: '服务', file: 'menu_province_center.png' },
|
||||
{ name: '省级合伙人', sub: '合伙', file: 'menu_province_partner.png' },
|
||||
{ name: '大区分公司', sub: '分公司', file: 'menu_region_branch.png' },
|
||||
];
|
||||
|
||||
expectedItems.forEach(item => {
|
||||
const targetUrl = `/pages/public/richtext?id=5&title=${item.name}`;
|
||||
const [page, query] = targetUrl.split('?');
|
||||
const params = new URLSearchParams(query);
|
||||
assert(page === '/pages/public/richtext', `路由路径正确: ${item.name}`);
|
||||
assert(params.get('id') === '5', `指向待开发富文本记录 ID 5: ${item.name}`);
|
||||
assert(params.get('title') === item.name, `导航栏标题参数正确回显: ${item.name}`);
|
||||
});
|
||||
|
||||
// 4. API 模拟测试(正常场景、业务失败场景、参数错误场景)
|
||||
console.log("\n四、线上 API 模拟测试(正常/业务失败/边界条件):");
|
||||
|
||||
try {
|
||||
const remoteScript = `#!/bin/bash
|
||||
curl -s -k --resolve henglian.gynhc.com:443:127.0.0.1 -H 'platform: WechatMiniProgram' 'https://henglian.gynhc.com/addons/shopro/index/init' > /tmp/api_test_init.json
|
||||
curl -s -k --resolve henglian.gynhc.com:443:127.0.0.1 'https://henglian.gynhc.com/addons/shopro/data.richtext?id=5' > /tmp/api_test_rich5.json
|
||||
curl -s -k --resolve henglian.gynhc.com:443:127.0.0.1 'https://henglian.gynhc.com/addons/shopro/data.richtext?id=999999' > /tmp/api_test_notfound.json
|
||||
curl -s -k --resolve henglian.gynhc.com:443:127.0.0.1 'https://henglian.gynhc.com/addons/shopro/data.richtext' > /tmp/api_test_empty.json
|
||||
`;
|
||||
|
||||
fs.writeFileSync('/tmp/local_test_api.sh', remoteScript);
|
||||
execSync(`sshpass -p 'Bjsslxz1' scp -o StrictHostKeyChecking=no /tmp/local_test_api.sh root@118.184.172.94:/tmp/test_api.sh`);
|
||||
execSync(`sshpass -p 'Bjsslxz1' ssh -o StrictHostKeyChecking=no root@118.184.172.94 "bash /tmp/test_api.sh"`);
|
||||
|
||||
|
||||
const fetchCmd = `sshpass -p 'Bjsslxz1' ssh -o StrictHostKeyChecking=no root@118.184.172.94 "cat /tmp/api_test_init.json && echo '===SEP===' && cat /tmp/api_test_rich5.json && echo '===SEP===' && cat /tmp/api_test_notfound.json && echo '===SEP===' && cat /tmp/api_test_empty.json"`;
|
||||
const fetched = execSync(fetchCmd, { timeout: 20000 }).toString();
|
||||
const parts = fetched.split('===SEP===').map(p => p.trim());
|
||||
|
||||
const initRes = JSON.parse(parts[0]);
|
||||
const richtextRes = JSON.parse(parts[1]);
|
||||
const notFoundRes = JSON.parse(parts[2]);
|
||||
const emptyIdRes = JSON.parse(parts[3]);
|
||||
|
||||
assert(initRes.code === 1, '正常场景: index/init 接口返回 code=1 成功');
|
||||
|
||||
const homeComp = initRes.data?.template?.home?.data || [];
|
||||
const menuBtn = homeComp.find(c => c.type === 'menuButton');
|
||||
assert(!!menuBtn, '正常场景: 首页装修模板中包含 menuButton 组件');
|
||||
|
||||
const list = menuBtn?.data?.list || [];
|
||||
assert(list.length === 5, `正常场景: menuButton 包含 5 个金刚区项目(实际 ${list.length} 个)`);
|
||||
|
||||
const titles = list.map(item => item.title?.text);
|
||||
const expectedTitles = ['市级运营中心', '市级合伙人', '省级服务中心', '省级合伙人', '大区分公司'];
|
||||
assert(JSON.stringify(titles) === JSON.stringify(expectedTitles),
|
||||
`正常场景: 金刚区 5 个项目标题完全匹配需求: ${titles.join(' | ')}`);
|
||||
|
||||
const allUrlsCorrect = list.every((item, idx) => item.url === `/pages/public/richtext?id=5&title=${expectedTitles[idx]}`);
|
||||
assert(allUrlsCorrect, '正常场景: 5 个项目的跳转链接均正确绑定富文本待开发页及对应标题');
|
||||
|
||||
// 场景 2:正常场景 - 富文本待开发数据接口
|
||||
assert(richtextRes.code === 1, '正常场景: data.richtext?id=5 接口返回 code=1 成功');
|
||||
assert(richtextRes.data?.title === '待开发,敬请期待', '正常场景: 富文本标题为“待开发,敬请期待”');
|
||||
assert(richtextRes.data?.content.includes('待开发,敬请期待'), '正常场景: 富文本内容包含“待开发,敬请期待”卡片');
|
||||
|
||||
// 场景 3:业务失败场景 - 请求不存在的富文本 ID
|
||||
assert(notFoundRes.code === 0, '业务失败场景: 请求不存在的 id=999999 返回 code=0 业务失败拦截');
|
||||
assert(!!notFoundRes.msg, `业务失败场景: 返回友好提示信息 (${notFoundRes.msg})`);
|
||||
|
||||
// 场景 4:参数边界场景 - 不传任何 ID
|
||||
assert(emptyIdRes.code === 0, '参数边界场景: 缺失 id 参数返回 code=0 并受全局异常/参数校验保护');
|
||||
|
||||
|
||||
} catch (err) {
|
||||
console.error("API 测试执行异常:", err.message);
|
||||
failed++;
|
||||
}
|
||||
|
||||
console.log("\n========================================================");
|
||||
console.log(`测试完成: 通过 ${passed} 项, 失败 ${failed} 项`);
|
||||
console.log("========================================================");
|
||||
|
||||
if (failed > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -14,16 +14,34 @@
|
||||
content: '',
|
||||
});
|
||||
|
||||
const defaultPendingContent = `<div style="padding: 60px 20px 40px; text-align: center; font-family: sans-serif;">
|
||||
<div style="width: 76px; height: 76px; margin: 0 auto 20px; border-radius: 50%; background: #FAF5EB; border: 1px solid #E5D3B3; display: flex; align-items: center; justify-content: center;">
|
||||
<span style="font-size: 34px; line-height: 76px;">⏳</span>
|
||||
</div>
|
||||
<div style="font-size: 20px; font-weight: bold; color: #262626; margin-bottom: 12px; letter-spacing: 1px;">待开发,敬请期待</div>
|
||||
<div style="font-size: 14px; color: #8C8C8C; line-height: 1.6; max-width: 280px; margin: 0 auto;">该业务板块正在加速筹备建设中<br/>更多专属权益即将上线</div>
|
||||
</div>`;
|
||||
|
||||
async function getRichTextContent(id) {
|
||||
const { code, data } = await sheep.$api.data.richtext(id);
|
||||
if (code === 1) {
|
||||
state.content = data.content;
|
||||
if (state.title === '') {
|
||||
state.title = data.title;
|
||||
uni.setNavigationBarTitle({
|
||||
title: state.title,
|
||||
});
|
||||
if (!id) {
|
||||
state.content = defaultPendingContent;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { code, data } = await sheep.$api.data.richtext(id);
|
||||
if (code === 1 && data?.content) {
|
||||
state.content = data.content;
|
||||
if (state.title === '') {
|
||||
state.title = data.title;
|
||||
uni.setNavigationBarTitle({
|
||||
title: state.title,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
state.content = defaultPendingContent;
|
||||
}
|
||||
} catch (e) {
|
||||
state.content = defaultPendingContent;
|
||||
}
|
||||
}
|
||||
onLoad((options) => {
|
||||
|
||||
@@ -276,8 +276,11 @@
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 30rpx;
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||