feat(miniapp): align UI with H5 and harden device debugging

五个页面按 h5/css 的 style.css → ui-20261009 → ui-20261012 覆盖顺序逐条重排,
换算规则固定为 calc(N * var(--u)) → 2N rpx,固定 px 的字号保持 px。

- app.acss:换回 H5 设计 token(方角小倒角按钮 10rpx、卡片 14rpx、.chev 折线箭头)
- splash/profile/history:底图与按钮文字图改用 H5 资源,隐私/政策弹窗按 H5 重排
- report:环形分用 conic-gradient、人脸裁切窗口叠加 face_maps、标记点百分比盒、
  侧脸皱纹 pending/unavailable 文案;用 Playwright 抓 H5 94 个选择器几何比对,
  整页高 2438.7 vs 2438.67
- camera:淘宝 <camera> 组件在真机整页崩溃(嵌了微信专有的 <cover-view>),
  改为默认系统相机 + `config.nativeCamera` 开关 + pages/camtest 冒烟页验证组件
- permissions:淘宝用带前缀的 scope.camera(裸名返回 5022 Scope:camera信息不存在),
  先试带前缀再退回裸名,拒绝后不再重复弹窗
- voice:非 HTTPS 资源导致的异常不再冒泡成整页崩溃;app.js 加开发版错误 toast
- 开发版入口:权限诊断(启动页/相机页)、相机组件测试
- 测试 20/20(新增权限与相机能力用例)
This commit is contained in:
xsl
2026-11-01 23:30:38 +08:00
parent f5c416dd9d
commit fed52930cc
33 changed files with 3061 additions and 876 deletions
+207 -4
View File
@@ -1,5 +1,208 @@
/* ═════ 历史报告 —— 逐条对照 H5:
style.css .card(L355-358) .toast(L588-599) .policy-header .history-clear
ui-20261009.css L8(#s-history 字体栈) L33-44(.history-content/.history-count/.identity-note/
.history-item/.history-detail/.history-retry) L99-106(.font-credit/.account-status)
ui-20261009.css L47(.report-title 全局:font-size 23px / padding 0 / margin-bottom)
ui-20261009.css L92(.report-title font-weight 600)
换算:calc(N * var(--u)) → 2N rpx;字号/行高/圆角固定 px 原样保留。 ═════ */
page { background: #EFEFEF; }
.history { padding-bottom: 48rpx; }
.history-note { padding: 32rpx; color: #666; text-align: center; }
.history-item { line-height: 1.9; }
.history-open { color: #666; text-align: right; }
/* H5 .screen#s-history */
.history {
position: relative;
min-height: 100vh;
background: #efefef;
}
/* H5 #s-history 的 background-image center/cover + background-attachment:fixed
→ 等价实现:fixed 定位、铺满视口的 image */
.history-bg {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 0;
}
/* H5 .history-content(ui-20261009 L34) */
.history-content {
position: relative;
z-index: 1;
padding: 56rpx 34rpx 72rpx; /* calc(28*u) calc(17*u) calc(36*u) */
box-sizing: border-box;
}
/* H5 .report-title(style.css L350-354 + ui-20261009 L47/L92) */
.report-title {
font-size: 23px;
font-weight: 600;
color: #282828;
text-align: center;
letter-spacing: 1rpx; /* calc(.5 * var(--u)) */
padding: 0;
margin-bottom: 46rpx; /* calc(23 * var(--u)) */
}
/* H5 .card(style.css L355-358 + ui-20261009 L48:圆角 calc(7*u)) */
.history .card {
background: #fff;
border-radius: 14rpx;
margin-bottom: 24rpx; /* calc(12 * var(--u)) */
box-sizing: border-box;
}
/* H5 .account-status / .account-status-history(ui-20261009 L100/L106) */
.account-status {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 14px;
border-radius: 8px;
font-size: 12px;
box-sizing: border-box;
}
.account-status-history {
width: 100%;
background: #fff;
margin-bottom: 14px;
text-align: left;
}
.account-label { display: flex; align-items: center; }
.account-dot {
display: inline-block;
width: 7px;
height: 7px;
margin-right: 7px;
border-radius: 50%;
background: #b7bac0;
flex-shrink: 0;
}
.account-status-on .account-dot { background: #34ca7b; }
.account-text { white-space: nowrap; }
/* H5 .account-logout(ui-20261012 L4) */
.account-logout {
font-size: 11px;
opacity: .8;
white-space: nowrap;
}
/* H5 .history-count(ui-20261009 L35-38)
H5 里 .history-count 没设 font-size,行盒取浏览器默认 16px;app.acss 把 page 设成
14px,所以这里显式补回 16px,内联的「测试数量」才会落在同一基线上。 */
.history .history-count {
text-align: center;
padding: 32rpx 24rpx 42rpx; /* calc(16*u) calc(12*u) calc(21*u) */
font-size: 16px;
}
/* H5 .history-count > span 是内联元素(内含 display:block 的 ::after 分隔线) */
.history-count-label {
display: inline;
color: #888;
font-size: 12px;
}
/* H5 .history-count > span::after */
.history-count-label::after {
content: '';
display: block;
width: 90rpx; /* calc(45 * var(--u)) */
margin: 24rpx auto; /* calc(12 * var(--u)) auto */
border-bottom: 1px solid #eee;
}
.history-count-value { font-size: 12px; }
/* H5 .identity-note(ui-20261009 L39) */
.identity-note {
color: #888;
font-size: 10px;
line-height: 1.6;
margin: 24rpx 8rpx 40rpx; /* calc(12*u) calc(4*u) calc(20*u) */
}
/* H5 .history-empty(style.css L637) */
.history-empty {
text-align: center;
color: #868686;
font-size: 12.5px;
padding: 60px 0;
}
/* H5 的加载中/失败文案是直接写进 #history-list 的纯文本(继承 16px、左对齐、无内边距) */
.history-list-note {
font-size: 16px;
color: #282828;
}
/* H5 .history-item(ui-20261009 L40-42) */
.history .history-item {
padding: 26rpx 38rpx 20rpx; /* calc(13*u) calc(19*u) calc(10*u) */
overflow: hidden;
margin-top: 12rpx; /* calc(6 * var(--u)) */
}
.history-item-line {
font-size: 12px;
padding-bottom: 14rpx; /* calc(7 * var(--u)) */
border-bottom: 1px solid #eee;
}
.history-item-label {
color: #888;
margin-right: 16rpx; /* calc(8 * var(--u)) */
}
/* H5 .history-detail(ui-20261009 L43) */
.history .history-detail {
display: block;
margin: 14rpx 0 0 auto; /* calc(7 * var(--u)) 0 0 auto */
background: #F5F078;
border-radius: 10rpx; /* calc(5 * var(--u)) */
width: 160rpx; /* calc(80 * var(--u)) */
min-height: 46rpx; /* calc(23 * var(--u)) */
font-size: 12px;
color: #282828;
line-height: 46rpx;
text-align: center;
padding: 0;
border: none;
box-sizing: border-box;
}
.history .history-detail::after { border: none; }
/* H5 .history-retry(ui-20261009 L44) */
.history .history-retry {
display: block;
width: fit-content; /* H5 是 block + width:auto 的按钮 → 收缩到文字宽再 margin:auto 居中 */
margin: 20px auto;
text-align: center;
color: #707070;
text-decoration: underline;
font-size: 12px;
background: none;
border: none;
padding: 0;
line-height: normal;
box-sizing: border-box;
}
.history .history-retry::after { border: none; }
/* ─── H5 .toast ─── */
.toast {
position: fixed;
left: 50%;
bottom: 15%;
transform: translateX(-50%) translateY(8px);
max-width: 86%;
background: rgba(20, 20, 20, .85);
color: #fff;
font-size: 13px;
line-height: 1.5;
text-align: center;
padding: 9px 18px;
border-radius: 18px;
opacity: 0;
pointer-events: none;
transition: all .25s ease;
z-index: 99;
box-sizing: border-box;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
+45 -7
View File
@@ -1,10 +1,48 @@
<!-- ═════ 历史报告 —— 对照 H5 index.html #s-history(L32-42) 与 app.js renderHistoryList()(L1956-1985) ═════ -->
<view class="history">
<view class="history-note">最近5份检测报告</view>
<view a:if="{{loading}}" class="history-note">加载中…</view>
<view a:if="{{error}}" class="history-note" onTap="load">{{error}},点击重试</view>
<view a:if="{{!loading && !error && !items.length}}" class="history-note">暂无检测报告</view>
<view class="card history-item" a:for="{{items}}" a:key="id" data-id="{{item.id}}" onTap="onOpen">
<view>{{item.date}}</view><view>综合评分 {{item.total}} · 肌肤年龄 {{item.skinAge}} 岁</view>
<view class="history-open">查看报告 ›</view>
<!-- H5 #s-history{background:#efefef url(bg-history-h5) center/cover fixed} -->
<image class="history-bg" src="{{bgUrl}}" mode="aspectFill" />
<!-- H5 .history-content -->
<view class="history-content">
<view class="report-title">您的报告</view>
<!-- H5 .account-status.account-status-history -->
<view class="account-status account-status-history {{signedIn ? 'account-status-on' : ''}}">
<view class="account-label">
<view class="account-dot"></view>
<text class="account-text">{{accountLabel}}</text>
</view>
<view class="account-logout" a:if="{{signedIn}}" onTap="onLogout">退出登录</view>
</view>
<!-- H5 .card.history-count -->
<view class="card history-count">
<view class="history-count-label">测试数量</view>
<view class="history-count-value">{{countText}}</view>
</view>
<!-- H5 .identity-note -->
<view class="identity-note">{{identityNote}}</view>
<!-- H5 #history-list -->
<view class="history-list">
<view class="history-list-note" a:if="{{loading && !items.length}}">正在读取报告…</view>
<view class="history-list-note" a:if="{{!loading && error}}">{{error}},请刷新列表重试。</view>
<view class="history-empty" a:if="{{!loading && !error && !items.length}}">
<view>暂无检测报告</view>
<view>完成一次检测后,报告会自动保存在这里。</view>
</view>
<view class="card history-item" a:for="{{items}}" a:key="id" data-id="{{item.id}}" onTap="onOpen">
<view class="history-item-line"><text class="history-item-label">测试时间</text> {{item.date}}</view>
<button class="history-detail" data-id="{{item.id}}" catchTap="onOpen">查看详情</button>
</view>
</view>
<!-- H5 .history-retry -->
<button class="history-retry" onTap="load">刷新列表</button>
</view>
<!-- H5 .toast -->
<view class="toast {{toastOn ? 'show' : ''}}">{{toastText}}</view>
</view>
+57 -10
View File
@@ -1,34 +1,81 @@
/* ═════ 历史报告:api.request('reports') 拉列表 + 点条目进报告页
对照 H5 index.html #s-history(L32-42) 与 app.js renderHistoryList()/openHistory()(L1944-2001)
跳转前把 payload 放进 globalData,由 pages/report 渲染。 ═════ */
var api = require('../../utils/api');
function pad2(n) { return (n < 10 ? '0' : '') + n; }
// H5 用 toLocaleDateString('sv-SE'),即 YYYY-MM-DD 零填充
function formatDate(value) {
var d = new Date(value);
return d.getFullYear() + '-' + pad2(d.getMonth() + 1) + '-' + pad2(d.getDate());
}
Page({
data: { items: [], loading: false, error: '' },
data: {
bgUrl: api.assetUrl('img/ui-20261009/bg-history-h5.jpg'),
items: [], loading: false, error: '',
signedIn: false, accountLabel: '尚未登录',
countText: '正在读取报告…', // H5 #history-count 初值
identityNote: '',
toastText: '', toastOn: false
},
onShow: function () {
if (!api.user()) { this.setData({ items: [] }); my.redirectTo({ url: '/pages/splash/splash' }); return; }
this.setData({ signedIn: true, accountLabel: '淘宝已登录' });
this.load();
},
onUnload: function () { this.closed = true; },
// H5 .toast
toast: function (msg) {
var that = this;
this.setData({ toastText: msg, toastOn: true });
clearTimeout(this.toastTimer);
this.toastTimer = setTimeout(function () {
that.setData({ toastOn: false });
}, Math.max(1800, String(msg).length * 130));
},
load: function () {
var that = this;
this.closed = false;
this.setData({ loading: true, error: '' });
api.request('reports').then(function (data) {
if (that.closed) return;
that.setData({ items: data.items.map(function (item) {
var date = new Date(item.created_at);
return { id: item.id, total: item.total == null ? '--' : item.total, skinAge: item.skinAge == null ? '--' : item.skinAge,
date: date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() };
}) });
}).catch(function (err) { if (!that.closed) that.setData({ error: err.message }); })
.then(function () { if (!that.closed) that.setData({ loading: false }); });
that.setData({
countText: '您最近已进行过' + data.total + '次面部问题检测',
identityNote: '保留最近 5 份报告,同一淘宝账号可跨手机查看。',
items: data.items.map(function (item) {
return { id: item.id, date: formatDate(item.created_at) };
})
});
}).catch(function (err) {
if (!that.closed) that.setData({ error: err.message, countText: '暂时无法读取' });
}).then(function () { if (!that.closed) that.setData({ loading: false }); });
},
onOpen: function (e) {
if (this.data.loading) return;
var id = e.currentTarget.dataset.id;
if (!id) return;
var that = this;
this.closed = false;
this.setData({ loading: true });
api.request('reports', { id: e.currentTarget.dataset.id }).then(function (data) {
api.request('reports', { id: id }).then(function (data) {
if (that.closed) return;
getApp().globalData.photos = data.report.photos;
getApp().globalData.payload = Object.assign({}, data.report.payload, { vm: data.report.vm, presentation: data.report.presentation });
my.navigateTo({ url: '/pages/report/report' });
}).catch(function (err) { my.showToast({ content: err.message, type: 'none' }); })
}).catch(function (err) { that.toast(err.message); })
.then(function () { if (!that.closed) that.setData({ loading: false }); });
},
// H5 data-action="logout-wechat"(历史页右上角)
onLogout: function () {
var that = this;
api.logout().then(function () {
that.setData({ signedIn: false, accountLabel: '尚未登录', items: [], countText: '', identityNote: '' });
my.redirectTo({ url: '/pages/splash/splash' });
}).catch(function (err) { that.toast(err.message); });
}
});
+1 -1
View File
@@ -1 +1 @@
{ "defaultTitle": "历史报告", "backgroundColor": "#EFEFEF" }
{ "defaultTitle": "历史报告", "titleBarColor": "#FFFFFF", "backgroundColor": "#EFEFEF" }