Files
hms/apps/miniprogram/verify-deep2.cjs
iven 50eae8b809 feat(miniprogram): 温润东方风全面 UI 重设计
73 文件变更,覆盖全部 40 个页面 SCSS + TabBar 图标 + 组件样式。
统一赤陶主色 #C4623A + 暖米背景 + 衬线标题字体 + 12px 圆角体系。
2026-04-28 00:19:52 +08:00

376 lines
14 KiB
JavaScript

const automator = require('miniprogram-automator');
const { execSync } = require('child_process');
const TIMEOUT = 12000;
function withTimeout(promise, ms, label) {
return Promise.race([promise, new Promise((_, r) => setTimeout(() => r(new Error(label + ' timeout')), ms))]);
}
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
async function checkElements(page, selectors) {
const results = {};
for (const [name, selector] of Object.entries(selectors)) {
try {
const el = await page.$(selector);
if (el) {
const text = (await el.textContent()).trim().replace(/\s+/g, ' ');
results[name] = { found: true, text };
} else {
results[name] = { found: false, text: '' };
}
} catch (e) {
results[name] = { found: false, text: '', error: e.message };
}
}
return results;
}
async function checkMultiElements(page, selector) {
try {
const els = await page.$$(selector);
const items = [];
for (let i = 0; i < els.length; i++) {
try {
const text = (await els[i].textContent()).trim().replace(/\s+/g, ' ');
items.push(text);
} catch {}
}
return items;
} catch {
return [];
}
}
async function main() {
console.log('=== HMS 深度验证 (元素级) ===\n');
// Restart automation
console.log('启动自动化...');
try {
execSync('"D:/微信web开发者工具/cli.bat" auto --project "g:/hms/apps/miniprogram" --auto-port 9420', { stdio: 'pipe', timeout: 15000 });
} catch {}
// Retry connection
let mp;
for (let attempt = 1; attempt <= 5; attempt++) {
await sleep(5000);
try {
mp = await withTimeout(automator.connect({ wsEndpoint: 'ws://127.0.0.1:9420' }), TIMEOUT, 'connect');
console.log('[OK] 已连接 (第' + attempt + '次尝试)\n');
break;
} catch (e) {
console.log('连接失败 (第' + attempt + '次)...');
if (attempt === 5) throw e;
}
}
const issues = [];
// ═══ 1. 首页 ═══
console.log('━━━ 1. 首页 ━━━');
try {
await mp.switchTab('/pages/index/index');
await sleep(3000);
const page = await mp.currentPage();
console.log(' 路径:', page.path);
// 等待页面渲染
try { await page.waitFor('.index-page', { timeout: 5000 }); } catch {}
const els = await checkElements(page, {
greeting: '.greeting-hello',
gName: '.greeting-name',
gDate: '.greeting-date',
healthTitle: '.section-title',
healthItem: '.health-item',
serviceItem: '.service-item',
});
console.log(' 问候语:', els.greeting.found ? '✓ ' + els.greeting.text : '✗ 缺失');
console.log(' 用户名:', els.gName.found ? '✓ ' + els.gName.text : '✗ 缺失');
console.log(' 日期:', els.gDate.found ? '✓ ' + els.gDate.text : '✗ 缺失');
const healthItems = await checkMultiElements(page, '.health-item');
console.log(' 健康卡片:', healthItems.length > 0 ? '✓ ' + healthItems.length + '个' : '✗ 缺失');
if (healthItems.length > 0) {
healthItems.forEach((h, i) => console.log(' [' + i + ']', h));
}
const services = await checkMultiElements(page, '.service-item');
console.log(' 快捷服务:', services.length > 0 ? '✓ ' + services.length + '个' : '✗ 缺失');
if (services.length > 0) {
services.forEach((s, i) => console.log(' [' + i + ']', s));
}
if (!els.greeting.found) issues.push('首页: 缺少问候语');
if (healthItems.length === 0) issues.push('首页: 缺少健康数据卡片');
if (services.length === 0) issues.push('首页: 缺少快捷服务');
} catch (e) {
console.log(' [FAIL]', e.message);
issues.push('首页: ' + e.message);
}
// ═══ 2. 健康中心 ═══
console.log('\n━━━ 2. 健康中心 ━━━');
try {
await mp.switchTab('/pages/health/index');
await sleep(3000);
const page = await mp.currentPage();
console.log(' 路径:', page.path);
try { await page.waitFor('.health-page', { timeout: 5000 }); } catch {}
const els = await checkElements(page, {
header: '.health-header-title',
inputBtn: '.health-header-btn-text',
card: '.health-card',
action: '.action-card',
});
console.log(' 标题:', els.header.found ? '✓ ' + els.header.text : '✗');
const cards = await checkMultiElements(page, '.health-card');
console.log(' 健康卡片:', cards.length > 0 ? '✓ ' + cards.length + '个' : '✗');
const actions = await checkMultiElements(page, '.action-card');
console.log(' 趋势入口:', actions.length > 0 ? '✓ ' + actions.length + '个' : '✗');
if (actions.length > 0) actions.forEach((a, i) => console.log(' [' + i + ']', a));
if (cards.length === 0) issues.push('健康中心: 缺少健康数据卡片');
if (actions.length === 0) issues.push('健康中心: 缺少趋势快捷入口');
} catch (e) {
console.log(' [FAIL]', e.message);
issues.push('健康中心: ' + e.message);
}
// ═══ 3. 健康数据录入 ═══
console.log('\n━━━ 3. 健康数据录入 ━━━');
try {
await mp.reLaunch('/pages/health/input/index');
await sleep(3000);
const page = await mp.currentPage();
console.log(' 路径:', page.path);
const els = await checkElements(page, {
label: '.input-label',
picker: '.input-picker',
field: '.input-field',
submit: '.input-submit',
});
console.log(' 表单标签:', els.label.found ? '✓' : '✗');
console.log(' 指标选择器:', els.picker.found ? '✓ ' + els.picker.text : '✗');
console.log(' 数值输入:', els.field.found ? '✓' : '✗');
console.log(' 提交按钮:', els.submit.found ? '✓ ' + els.submit.text : '✗');
if (!els.picker.found) issues.push('健康录入: 缺少指标选择器');
if (!els.field.found) issues.push('健康录入: 缺少数值输入框');
if (!els.submit.found) issues.push('健康录入: 缺少提交按钮');
} catch (e) {
console.log(' [FAIL]', e.message);
issues.push('健康录入: ' + e.message);
}
// ═══ 4. 创建预约 ═══
console.log('\n━━━ 4. 创建预约 ━━━');
try {
await mp.reLaunch('/pages/appointment/create/index');
await sleep(3000);
const page = await mp.currentPage();
console.log(' 路径:', page.path);
const stepEl = await checkElements(page, {
stepTitle: '.step-title',
deptCard: '.dept-card',
nextBtn: '.btn-next',
});
console.log(' 步骤标题:', stepEl.stepTitle.found ? '✓ ' + stepEl.stepTitle.text : '✗');
const deptCards = await checkMultiElements(page, '.dept-card');
console.log(' 科室卡片:', deptCards.length > 0 ? '✓ ' + deptCards.length + '个' : '✗');
if (deptCards.length > 0) {
deptCards.forEach((d, i) => console.log(' [' + i + ']', d));
}
console.log(' "下一步"按钮:', stepEl.nextBtn.found ? '✓' : '✗');
if (deptCards.length === 0) issues.push('创建预约: 缺少科室选择卡片');
if (!stepEl.nextBtn.found) issues.push('创建预约: 缺少下一步按钮');
} catch (e) {
console.log(' [FAIL]', e.message);
issues.push('创建预约: ' + e.message);
}
// ═══ 5. 预约列表 ═══
console.log('\n━━━ 5. 预约列表 ━━━');
try {
await mp.switchTab('/pages/appointment/index');
await sleep(3000);
const page = await mp.currentPage();
console.log(' 路径:', page.path);
const els = await checkElements(page, {
title: '.page-title',
fab: '.fab-btn',
empty: '.empty-text',
list: '.appointment-list',
});
console.log(' 标题:', els.title.found ? '✓ ' + els.title.text : '✗');
console.log(' 新建预约按钮:', els.fab.found ? '✓' : '✗');
console.log(' 空状态:', els.empty.found ? '✓ ' + els.empty.text : ' (有数据)');
const cards = await checkMultiElements(page, '.appointment-card');
console.log(' 预约卡片:', cards.length + '个');
if (!els.fab.found) issues.push('预约列表: 缺少新建预约按钮');
} catch (e) {
console.log(' [FAIL]', e.message);
issues.push('预约列表: ' + e.message);
}
// ═══ 6. 资讯文章 ═══
console.log('\n━━━ 6. 资讯文章 ━━━');
try {
await mp.switchTab('/pages/article/index');
await sleep(3000);
const page = await mp.currentPage();
console.log(' 路径:', page.path);
const articles = await checkMultiElements(page, '.article-card');
const empty = await checkElements(page, { e: '.empty-text' });
console.log(' 文章卡片:', articles.length + '个');
console.log(' 空状态:', empty.e.found ? '✓ ' + empty.e.text : '');
if (articles.length > 0) {
articles.forEach((a, i) => console.log(' [' + i + ']', a.substring(0, 50)));
}
} catch (e) {
console.log(' [FAIL]', e.message);
issues.push('资讯文章: ' + e.message);
}
// ═══ 7. 个人中心 ═══
console.log('\n━━━ 7. 个人中心 ━━━');
try {
await mp.switchTab('/pages/profile/index');
await sleep(3000);
const page = await mp.currentPage();
console.log(' 路径:', page.path);
const els = await checkElements(page, {
avatar: '.profile-avatar-text',
name: '.profile-name',
menu: '.menu-item',
logout: '.logout-text',
});
console.log(' 头像:', els.avatar.found ? '✓ ' + els.avatar.text : '✗');
console.log(' 用户名:', els.name.found ? '✓ ' + els.name.text : '✗');
const menuItems = await checkMultiElements(page, '.menu-item');
console.log(' 菜单项:', menuItems.length > 0 ? '✓ ' + menuItems.length + '个' : '✗');
if (menuItems.length > 0) {
menuItems.forEach((m, i) => console.log(' [' + i + ']', m));
}
console.log(' 退出登录:', els.logout.found ? '✓ ' + els.logout.text : '✗');
if (menuItems.length === 0) issues.push('个人中心: 缺少菜单项');
if (!els.logout.found) issues.push('个人中心: 缺少退出登录按钮');
} catch (e) {
console.log(' [FAIL]', e.message);
issues.push('个人中心: ' + e.message);
}
// ═══ 8. 就诊人管理 ═══
console.log('\n━━━ 8. 就诊人管理 ━━━');
try {
await mp.reLaunch('/pages/profile/family/index');
await sleep(3000);
const page = await mp.currentPage();
console.log(' 路径:', page.path);
const addBtn = await checkElements(page, { btn: '.family-add-text' });
console.log(' "添加就诊人":', addBtn.btn.found ? '✓' : '✗');
const patients = await checkMultiElements(page, '.family-item');
console.log(' 就诊人列表:', patients.length + '个');
if (!addBtn.btn.found) issues.push('就诊人管理: 缺少添加按钮');
} catch (e) {
console.log(' [FAIL]', e.message);
issues.push('就诊人管理: ' + e.message);
}
// ═══ 9. 登录页 ═══
console.log('\n━━━ 9. 登录页 ━━━');
try {
await mp.reLaunch('/pages/login/index');
await sleep(3000);
const page = await mp.currentPage();
console.log(' 路径:', page.path);
const els = await checkElements(page, {
logo: '.login-logo-text',
title: '.login-title',
subtitle: '.login-subtitle',
btn: '.login-btn',
agreement: '.agreement-text',
checkbox: '.checkbox',
userLink: '.agreement-link',
});
console.log(' Logo:', els.logo.found ? '✓ ' + els.logo.text : '✗');
console.log(' 标题:', els.title.found ? '✓ ' + els.title.text : '✗');
console.log(' 副标题:', els.subtitle.found ? '✓ ' + els.subtitle.text : '✗');
console.log(' 登录按钮:', els.btn.found ? '✓' : '✗');
console.log(' 协议勾选:', els.checkbox.found ? '✓' : '✗');
console.log(' 协议文案:', els.agreement.found ? '✓ ' + els.agreement.text.substring(0, 30) : '✗');
const links = await checkMultiElements(page, '.agreement-link');
console.log(' 协议链接:', links.length > 0 ? '✓ ' + links.length + '个' : '✗');
if (!els.btn.found) issues.push('登录页: 缺少登录按钮');
if (!els.checkbox.found) issues.push('登录页: 缺少协议勾选框');
if (links.length < 2) issues.push('登录页: 缺少用户协议/隐私政策链接');
} catch (e) {
console.log(' [FAIL]', e.message);
issues.push('登录页: ' + e.message);
}
// ═══ 10. 法律页面 ═══
console.log('\n━━━ 10. 用户协议 & 隐私政策 ━━━');
for (const [name, path] of [['用户协议', '/pages/legal/user-agreement'], ['隐私政策', '/pages/legal/privacy-policy']]) {
try {
await mp.reLaunch(path);
await sleep(2000);
const page = await mp.currentPage();
const views = await checkMultiElements(page, 'view');
const hasContent = views.some(v => v.length > 10);
console.log(' ' + name + ':', hasContent ? '✓ 有内容' : '✗ 内容为空');
if (!hasContent) issues.push(name + ': 内容为空');
} catch (e) {
console.log(' ' + name + ': [FAIL]', e.message);
issues.push(name + ': ' + e.message);
}
}
// ═══ 汇总 ═══
console.log('\n╔══════════════════════════════════════╗');
if (issues.length === 0) {
console.log('║ ✓ 全部页面内容验证通过,无问题发现 ║');
} else {
console.log('║ 发现 ' + issues.length + ' 个问题: ║');
issues.forEach((issue, i) => {
console.log('║ ' + (i + 1) + '. ' + issue.substring(0, 30).padEnd(32) + '║');
});
}
console.log('╚══════════════════════════════════════╝');
// 不用 mp.close() 避免关闭自动化
mp.disconnect();
}
main().catch(err => { console.error('Fatal:', err.message); process.exit(1); });