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

300 lines
13 KiB
JavaScript

const automator = require('miniprogram-automator');
const { execSync } = require('child_process');
const TIMEOUT = 10000;
function wt(p, ms, label) { return Promise.race([p, new Promise((_, r) => setTimeout(() => r(new Error(label + ' timeout')), ms))]); }
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
async function trySelect(page, sel) {
try {
const el = await wt(page.$(sel), 3000, sel);
if (!el) return null;
const text = (await wt(el.textContent(), 3000, 'text')).trim().replace(/\s+/g, ' ');
return text;
} catch { return null; }
}
async function trySelectAll(page, sel) {
try {
const els = await wt(page.$$(sel), 3000, sel);
const items = [];
for (let i = 0; i < els.length; i++) {
try { items.push((await els[i].textContent()).trim().replace(/\s+/g, ' ')); } catch {}
}
return items;
} catch { return []; }
}
async function main() {
console.log('=== HMS 深度验证 V3 ===\n');
let mp;
for (let i = 1; i <= 8; i++) {
try {
mp = await wt(automator.connect({ wsEndpoint: 'ws://127.0.0.1:9420' }), TIMEOUT, 'connect');
console.log('[OK] 连接成功\n');
break;
} catch { if (i === 8) { console.log('连接失败,请确认已运行: cli.bat auto --project ... --auto-port 9420'); return; } console.log('等待...(' + i + ')'); await sleep(3); }
}
const issues = [];
// === 首页 ===
console.log('━━━ 首页 ━━━');
try {
await wt(mp.switchTab('/pages/index/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const greeting = await trySelect(page, '.greeting-hello');
const gName = await trySelect(page, '.greeting-name');
const gDate = await trySelect(page, '.greeting-date');
const healthItems = await trySelectAll(page, '.health-item');
const services = await trySelectAll(page, '.service-item');
const emptyText = await trySelect(page, '.empty-text');
console.log(' 问候语:', greeting || '✗');
console.log(' 用户名:', gName || '✗');
console.log(' 日期:', gDate || '✗');
console.log(' 健康卡片:', healthItems.length > 0 ? healthItems.length + '个' : '✗');
healthItems.forEach(h => console.log(' -', h));
console.log(' 快捷服务:', services.length > 0 ? services.length + '个' : '✗');
services.forEach(s => console.log(' -', s));
console.log(' 空状态:', emptyText || '(无)');
if (!greeting) issues.push('首页缺少问候语');
if (healthItems.length === 0) issues.push('首页缺少健康数据卡片(应有4个)');
if (services.length === 0) issues.push('首页缺少快捷服务(应有4个)');
} catch (e) { console.log(' FAIL:', e.message); issues.push('首页:' + e.message); }
// === 健康中心 ===
console.log('\n━━━ 健康中心 ━━━');
try {
await wt(mp.switchTab('/pages/health/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const header = await trySelect(page, '.health-header-title');
const inputBtn = await trySelect(page, '.health-header-btn-text');
const cards = await trySelectAll(page, '.health-card');
const actions = await trySelectAll(page, '.action-card');
console.log(' 标题:', header || '✗');
console.log(' 录入按钮:', inputBtn || '✗');
console.log(' 健康卡片:', cards.length > 0 ? cards.length + '个' : '✗');
cards.forEach(c => console.log(' -', c));
console.log(' 趋势入口:', actions.length > 0 ? actions.length + '个' : '✗');
actions.forEach(a => console.log(' -', a));
if (!header) issues.push('健康中心缺少标题');
if (cards.length === 0) issues.push('健康中心缺少数据卡片');
if (actions.length === 0) issues.push('健康中心缺少趋势入口');
} catch (e) { console.log(' FAIL:', e.message); issues.push('健康中心:' + e.message); }
// === 健康录入 ===
console.log('\n━━━ 健康录入 ━━━');
try {
await wt(mp.reLaunch('/pages/health/input/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const picker = await trySelect(page, '.input-picker');
const fields = await trySelectAll(page, '.input-field');
const submit = await trySelect(page, '.input-submit');
const labels = await trySelectAll(page, '.input-label');
console.log(' 指标选择器:', picker || '✗');
console.log(' 表单标签:', labels.length + '个');
labels.forEach(l => console.log(' -', l));
console.log(' 输入框:', fields.length + '个');
console.log(' 提交按钮:', submit || '✗');
if (!picker) issues.push('健康录入缺少指标选择器');
if (fields.length === 0) issues.push('健康录入缺少输入框');
if (!submit) issues.push('健康录入缺少提交按钮');
} catch (e) { console.log(' FAIL:', e.message); issues.push('健康录入:' + e.message); }
// === 健康趋势 ===
console.log('\n━━━ 健康趋势 ━━━');
try {
await wt(mp.reLaunch('/pages/health/trend/index?indicator=heart_rate'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const title = await trySelect(page, '.trend-title');
const tabs = await trySelectAll(page, '.trend-tab-text');
console.log(' 标题:', title || '✗');
console.log(' 时间Tab:', tabs.length > 0 ? tabs.join(', ') : '✗');
if (!title) issues.push('健康趋势缺少标题');
if (tabs.length === 0) issues.push('健康趋势缺少时间范围选项(7天/30天/90天)');
} catch (e) { console.log(' FAIL:', e.message); issues.push('健康趋势:' + e.message); }
// === 预约列表 ===
console.log('\n━━━ 预约列表 ━━━');
try {
await wt(mp.switchTab('/pages/appointment/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const pageTitle = await trySelect(page, '.page-title');
const fab = await trySelect(page, '.fab-text');
const empty = await trySelect(page, '.empty-text');
const apptCards = await trySelectAll(page, '.appointment-card');
console.log(' 标题:', pageTitle || '✗');
console.log(' 悬浮按钮:', fab || '✗');
console.log(' 空状态:', empty || '(有数据)');
console.log(' 预约卡片:', apptCards.length + '个');
if (!pageTitle) issues.push('预约列表缺少标题');
if (!fab) issues.push('预约列表缺少新建按钮');
} catch (e) { console.log(' FAIL:', e.message); issues.push('预约列表:' + e.message); }
// === 创建预约 ===
console.log('\n━━━ 创建预约 ━━━');
try {
await wt(mp.reLaunch('/pages/appointment/create/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const stepTitle = await trySelect(page, '.step-title');
const depts = await trySelectAll(page, '.dept-card');
const nextBtn = await trySelect(page, '.btn-next');
console.log(' 步骤标题:', stepTitle || '✗');
console.log(' 科室卡片:', depts.length > 0 ? depts.length + '个' : '✗');
depts.forEach(d => console.log(' -', d));
console.log(' 下一步按钮:', nextBtn || '✗');
if (depts.length === 0) issues.push('创建预约缺少科室选择(应有6个)');
if (!nextBtn) issues.push('创建预约缺少下一步按钮');
} catch (e) { console.log(' FAIL:', e.message); issues.push('创建预约:' + e.message); }
// === 资讯文章 ===
console.log('\n━━━ 资讯文章 ━━━');
try {
await wt(mp.switchTab('/pages/article/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const articles = await trySelectAll(page, '.article-card');
const empty = await trySelect(page, '.empty-text');
console.log(' 文章卡片:', articles.length + '个');
if (articles.length > 0) articles.forEach((a, i) => console.log(' [' + i + ']', a.substring(0, 50)));
console.log(' 空状态:', empty || '(有数据)');
} catch (e) { console.log(' FAIL:', e.message); issues.push('资讯文章:' + e.message); }
// === 个人中心 ===
console.log('\n━━━ 个人中心 ━━━');
try {
await wt(mp.switchTab('/pages/profile/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const avatar = await trySelect(page, '.profile-avatar-text');
const pName = await trySelect(page, '.profile-name');
const menus = await trySelectAll(page, '.menu-item');
const logout = await trySelect(page, '.logout-text');
console.log(' 头像字符:', avatar || '✗');
console.log(' 用户名:', pName || '✗');
console.log(' 菜单项:', menus.length > 0 ? menus.length + '个' : '✗');
menus.forEach(m => console.log(' -', m));
console.log(' 退出按钮:', logout || '✗');
if (menus.length === 0) issues.push('个人中心缺少菜单项(应有5个)');
if (!logout) issues.push('个人中心缺少退出登录');
} catch (e) { console.log(' FAIL:', e.message); issues.push('个人中心:' + e.message); }
// === 就诊人管理 ===
console.log('\n━━━ 就诊人管理 ━━━');
try {
await wt(mp.reLaunch('/pages/profile/family/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const addBtn = await trySelect(page, '.family-add-text');
const patients = await trySelectAll(page, '.family-item');
const empty = await trySelect(page, '.empty-text');
console.log(' 添加按钮:', addBtn || '✗');
console.log(' 就诊人:', patients.length + '个');
console.log(' 空状态:', empty || '(有数据)');
if (!addBtn) issues.push('就诊人管理缺少添加按钮');
} catch (e) { console.log(' FAIL:', e.message); issues.push('就诊人管理:' + e.message); }
// === 我的报告 ===
console.log('\n━━━ 我的报告 ━━━');
try {
await wt(mp.reLaunch('/pages/profile/reports/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const reports = await trySelectAll(page, '.report-card');
const empty = await trySelect(page, '.empty-text');
console.log(' 报告卡片:', reports.length + '个');
console.log(' 空状态:', empty || '(有数据)');
} catch (e) { console.log(' FAIL:', e.message); issues.push('我的报告:' + e.message); }
// === 登录页 ===
console.log('\n━━━ 登录页 ━━━');
try {
await wt(mp.reLaunch('/pages/login/index'), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const logo = await trySelect(page, '.login-logo-text');
const lTitle = await trySelect(page, '.login-title');
const lSub = await trySelect(page, '.login-subtitle');
const lBtn = await trySelect(page, '.login-btn');
const agreement = await trySelect(page, '.agreement-text');
const checkbox = await trySelect(page, '.checkbox');
const links = await trySelectAll(page, '.agreement-link');
console.log(' Logo:', logo || '✗');
console.log(' 标题:', lTitle || '✗');
console.log(' 副标题:', lSub || '✗');
console.log(' 登录按钮:', lBtn ? '✓' : '✗');
console.log(' 协议文案:', agreement ? agreement.substring(0, 40) : '✗');
console.log(' 勾选框:', checkbox ? '✓' : '✗');
console.log(' 协议链接:', links.length + '个');
if (!lBtn) issues.push('登录页缺少登录按钮');
if (!checkbox) issues.push('登录页缺少协议勾选框');
if (links.length < 2) issues.push('登录页缺少用户协议/隐私政策链接(应有2个)');
} catch (e) { console.log(' FAIL:', e.message); issues.push('登录页:' + e.message); }
// === 法律页面 ===
console.log('\n━━━ 法律页面 ━━━');
for (const [name, path] of [['用户协议', '/pages/legal/user-agreement'], ['隐私政策', '/pages/legal/privacy-policy']]) {
try {
await wt(mp.reLaunch(path), TIMEOUT, 'nav');
await sleep(2);
const page = await wt(mp.currentPage(), TIMEOUT, 'page');
const views = await trySelectAll(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); }
}
// ═══ 汇总 ═══
console.log('\n═══════════════════════════════════════');
if (issues.length === 0) {
console.log(' 全部页面内容验证通过');
} else {
console.log(' 发现 ' + issues.length + ' 个问题:');
issues.forEach((issue, i) => console.log(' ' + (i + 1) + '. ' + issue));
}
console.log('═══════════════════════════════════════');
mp.disconnect();
}
main().catch(err => { console.error('Fatal:', err.message); process.exit(1); });