0bed761c00
toast改感谢您的反馈/D校验文案/提交失败解析后端detail/模拟键盘补小数点后2位上限/取消改closePage真返回/上报记录入口接SGBridge.navigate(reportRecords)/头部未读红点默认隐藏 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1788 lines
105 KiB
HTML
1788 lines
105 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||
<title>比价记录 · 傻瓜比价</title>
|
||
<style>
|
||
:root {
|
||
/* === 摘自 DESIGN.md === */
|
||
--color-primary: #FFD600;
|
||
--color-primary-dark: #FFB300;
|
||
--color-on-primary: #1A1A1A;
|
||
--color-ink: #1A1A1A;
|
||
--color-ink-secondary: #333333;
|
||
--color-ink-muted: #666666;
|
||
--color-ink-subtle: #999999;
|
||
--color-ink-disabled: #CCCCCC;
|
||
--color-ink-on-dark: #FFFFFF;
|
||
--color-surface-card: #FFFFFF;
|
||
--color-surface-page: #FFFDF5;
|
||
--color-surface-muted: #FAFAFA;
|
||
--color-divider: #F0F0F0;
|
||
--color-divider-strong: #E5E5E5;
|
||
--color-price-lowest: #FF3D00;
|
||
--color-price-lowest-bg: #FFEBEE;
|
||
--color-platform-taobao-shanguang: #FF6A00;
|
||
--color-platform-jd-waimai: #E1251B;
|
||
--color-platform-meituan-waimai: #FFC300;
|
||
--rounded-sm: 8px;
|
||
--rounded-md: 12px;
|
||
--rounded: 14px;
|
||
--rounded-pill: 999px;
|
||
--font-num: 'DIN Alternate', 'Helvetica Neue', sans-serif;
|
||
--shadow-cta-warm: inset 0 1px 0 rgba(255,255,255,.7), 0 2px 6px rgba(255,179,0,.35);
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
||
html, body { height: 100%; }
|
||
/* 【去框】原型 body 是黑底 + flex 居中(浏览器预览用)。WebView 里直接铺页面色、不居中。 */
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||
background: #F5F5F5;
|
||
color: var(--color-ink);
|
||
}
|
||
|
||
/* 【去框】原型 .device 是 375x812 的手机预览框(圆角 + 阴影 + 固定宽高)。
|
||
WebView 里改成全屏自适应容器:满宽、最少占满视口高、无圆角/阴影。 */
|
||
.device { position: relative; width: 100%; height: 100%; min-height: 100vh; background: #F5F5F5; overflow: hidden; }
|
||
|
||
/* status bar —— App 顶部有原生状态栏/标题栏,这里隐藏原型自带的假状态栏 */
|
||
.status-bar { display: none; }
|
||
|
||
/* header —— 标题居中 + 右侧搜索按钮 */
|
||
.hist-header { height: 44px; background: #F5F5F5; display: flex; align-items: center; justify-content: center; padding: 0 16px; position: relative; }
|
||
.hist-header-title { font-size: 17px; font-weight: 600; color: var(--color-ink); }
|
||
.hist-header-back { display: none; position: absolute; left: 0; top: 0; width: 44px; height: 44px; align-items: center; justify-content: center; background: transparent; border: none; padding: 0; cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
||
.hist-header-back:active { opacity: .5; }
|
||
.hist-header-back svg { width: 24px; height: 24px; display: block; }
|
||
.hist-header-search { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
|
||
.hist-header-search:active { opacity: .6; }
|
||
.hist-header-cancel { display: none; position: absolute; left: 12px; top: 50%; transform: translateY(-50%); height: 32px; padding: 0 8px; align-items: center; font-size: 15px; color: var(--color-ink-secondary); cursor: pointer; }
|
||
.hist-header-cancel:active { opacity: .5; }
|
||
.device.selecting .hist-header-cancel { display: flex; }
|
||
.hist-header-reports { display: none; position: absolute; right: 12px; top: 50%; transform: translateY(-50%); height: 32px; padding: 0 4px; align-items: center; gap: 4px; font-size: 14px; font-weight: 400; color: var(--color-ink-muted); background: none; border: none; font-family: inherit; cursor: pointer; }
|
||
.hist-header-reports:active { opacity: .5; }
|
||
.hist-header-reports-dot { width: 5px; height: 5px; border-radius: 50%; background: #FF3D00; flex-shrink: 0; align-self: flex-start; margin-top: 6px; }
|
||
.device.selecting .hist-header-reports { display: inline-flex; }
|
||
|
||
/* scroll area —— 【去框】原型用 absolute + 顶/底偏移定位(top:132 = header44+filter44+留白;bottom:60 = 给原生 tab-bar 留位)。
|
||
WebView 隐藏了底部 tab-bar,故 bottom 改 0。其余偏移沿用原型不动。 */
|
||
.hist-scroll { position: absolute; top: 88px; left: 0; right: 0; bottom: 0; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 8px 12px 24px; }
|
||
.device.profile-entry .hist-header-back { display: flex; }
|
||
.device.profile-entry .hist-scroll { bottom: 0; padding-bottom: 32px; }
|
||
.device.profile-entry .tab-bar { display: none; }
|
||
.device.profile-entry.logged-out .hist-empty-logout { bottom: 0; }
|
||
|
||
/* 顶部筛选 tab(仿淘宝订单页 tab + 下划线) */
|
||
.hist-filter { position: absolute; left: 0; right: 0; top: 44px; height: 44px; display: flex; align-items: center; padding: 0 16px; gap: 22px; background: #F5F5F5; z-index: 2; }
|
||
/* tab 字号字重对齐店名 15/600,激活态只改颜色 + 黄下划线,不改字号字重(避免跳字) */
|
||
.hist-filter-tab { font-size: 15px; color: var(--color-ink-muted); font-weight: 600; cursor: pointer; position: relative; height: 44px; line-height: 44px; padding: 0 2px; transition: color .15s ease; }
|
||
.hist-filter-tab:active { opacity: .6; }
|
||
.hist-filter-tab.active { color: var(--color-ink); }
|
||
.hist-filter-tab.active::after { content: ''; position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%); width: 20px; height: 3px; background: var(--color-primary); border-radius: 2px; }
|
||
.device.searching .hist-filter,
|
||
.device.selecting .hist-filter { display: none; }
|
||
.hist-scroll::-webkit-scrollbar { display: none; }
|
||
|
||
/* item —— 白卡 + 弱阴影 */
|
||
.hist-item { background: #FFFFFF; border-radius: 14px; padding: 14px 14px 10px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,.04); position: relative; }
|
||
|
||
/* item head: 店名 + 已下单状态;默认日期放在标题下方 */
|
||
.hist-item-head { display: flex; align-items: center; gap: 6px; }
|
||
.hist-store { font-size: 15px; font-weight: 600; color: var(--color-ink); line-height: 1.3; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.hist-date { font-size: 11px; color: var(--color-ink-subtle); font-weight: 400; line-height: 1.3; white-space: nowrap; }
|
||
.hist-item-meta { display: flex; align-items: center; gap: 6px; min-width: 0; }
|
||
.device.time-title .hist-item > .hist-item-meta { margin-top: 3px; }
|
||
.device.time-chip .hist-item-head .hist-item-meta { flex-shrink: 0; height: 16px; padding: 0 5px; border-radius: 4px; background: var(--color-surface-muted); }
|
||
.device.time-chip .hist-date { font-size: 10px; color: var(--color-ink-muted); }
|
||
.device.time-hidden .hist-item-meta { display: none; }
|
||
/* 已下单标记:浅绿小 chip,紧跟在店名后 */
|
||
.hist-ordered-chip { display: inline-flex; align-items: center; height: 16px; padding: 0 5px; border-radius: 3px; background: #E8F5E9; color: #2E7D32; font-size: 10px; font-weight: 600; flex-shrink: 0; }
|
||
|
||
/* dish 两种模式:
|
||
- dish-full:≤ 3 件,完整列出菜名,无入口
|
||
- dish-fold:≥ 4 件,菜名串单行 ellipsis + 等 N 件 + › 入口,点击在当前卡片内展开
|
||
*/
|
||
.hist-dish-full { font-size: 12px; color: var(--color-ink-muted); margin-top: 8px; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
||
|
||
.hist-dish-fold { position: relative; display: flex; align-items: center; margin-top: 8px; padding-right: 18px; cursor: pointer; user-select: none; min-width: 0; }
|
||
.hist-dish-fold:active { opacity: .55; }
|
||
.hist-dish-fold-names { font-size: 12px; color: var(--color-ink-muted); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.4; }
|
||
.hist-dish-fold-count { font-size: 12px; color: var(--color-ink-subtle); margin-left: 4px; flex-shrink: 0; line-height: 1.4; }
|
||
.hist-dish-fold-arrow { position: absolute; right: 0; top: 50%; width: 12px; height: 12px; color: var(--color-ink-disabled); display: flex; align-items: center; justify-content: center; transform: translateY(-50%); transition: transform .16s ease, color .16s ease; transform-origin: center; }
|
||
.hist-dish-fold-arrow svg { width: 12px; height: 12px; display: block; stroke: currentColor; overflow: visible; }
|
||
.hist-item.dish-expanded .hist-dish-fold { align-items: flex-start; }
|
||
.hist-item.dish-expanded .hist-dish-fold-names { white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.45; }
|
||
.hist-item.dish-expanded .hist-dish-fold-count { padding-top: 1px; }
|
||
.hist-item.dish-expanded .hist-dish-fold-arrow { top: 3px; color: var(--color-ink-subtle); transform: rotate(180deg); }
|
||
/* 单选模式下禁用 fold 入口,避免点击展开菜单与选中行冲突 */
|
||
.device.selecting .hist-dish-fold { pointer-events: none; }
|
||
.device.selecting .hist-dish-fold-arrow { visibility: hidden; }
|
||
|
||
/* menu sheet —— 菜品清单底部弹窗 */
|
||
.menu-mask { position: absolute; inset: 0; background: rgba(0,0,0,.45); z-index: 9; opacity: 0; pointer-events: none; transition: opacity .25s; }
|
||
.menu-mask.show { opacity: 1; pointer-events: auto; }
|
||
.menu-sheet { position: fixed; left: 0; right: 0; bottom: 0; background: #FFFFFF; border-top-left-radius: 18px; border-top-right-radius: 18px; padding: 8px 16px 28px; z-index: 10; transform: translateY(100%); transition: transform .3s cubic-bezier(.2,.8,.2,1); max-height: 70%; display: flex; flex-direction: column; }
|
||
.menu-sheet.show { transform: translateY(0); }
|
||
.menu-grip { width: 36px; height: 4px; background: rgba(0,0,0,.18); border-radius: 2px; margin: 8px auto 14px; }
|
||
.menu-title-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
|
||
.menu-title { font-size: 16px; font-weight: 600; color: var(--color-ink); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.menu-count { font-size: 12px; color: var(--color-ink-subtle); flex-shrink: 0; }
|
||
.menu-date { font-size: 12px; color: var(--color-ink-subtle); margin-bottom: 8px; }
|
||
.menu-list { overflow-y: auto; flex: 1; min-height: 0; -webkit-overflow-scrolling: touch; }
|
||
.menu-list::-webkit-scrollbar { display: none; }
|
||
.menu-dish { display: flex; align-items: baseline; justify-content: space-between; padding: 13px 0; border-bottom: 0.5px solid var(--color-divider); font-size: 14px; line-height: 1.4; }
|
||
.menu-dish:last-child { border-bottom: 0; }
|
||
.menu-dish-name { color: var(--color-ink); flex: 1; min-width: 0; padding-right: 12px; }
|
||
.menu-dish-qty { color: var(--color-ink-subtle); font-family: var(--font-num); flex-shrink: 0; }
|
||
.menu-close { margin-top: 12px; height: 44px; background: var(--color-surface-muted); border-radius: 999px; display: flex; align-items: center; justify-content: center; font-size: 15px; font-weight: 600; color: var(--color-ink); cursor: pointer; }
|
||
.menu-close:active { opacity: .7; }
|
||
|
||
/* platform grid —— 固定 3 列等宽,固定顺序:美团 / 淘宝 / 京东。"最低"角标跟着实际最低价走 */
|
||
.hist-platforms { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 12px; }
|
||
.hist-pl-cell { position: relative; display: flex; flex-direction: column; gap: 6px; padding: 10px 10px; border: 0.5px solid var(--color-divider-strong); border-radius: 10px; min-width: 0; background: var(--color-surface-card); }
|
||
.hist-pl-cell.lowest { border-color: var(--color-price-lowest); }
|
||
.hist-pl-head { display: flex; align-items: center; gap: 5px; min-width: 0; }
|
||
.hist-pl-logo { width: 14px; height: 14px; border-radius: 3px; object-fit: cover; flex-shrink: 0; }
|
||
.hist-pl-name { font-size: 11px; color: var(--color-ink-secondary); font-weight: 400; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
||
.hist-pl-price { font-family: var(--font-num); font-size: 15px; font-weight: 600; color: var(--color-ink); line-height: 1; }
|
||
.hist-pl-cell.lowest .hist-pl-price { color: var(--color-price-lowest); font-size: 16px; font-weight: 700; }
|
||
.hist-pl-cell.lowest .hist-pl-name { color: var(--color-ink); font-weight: 600; }
|
||
/* "最低" badge 浮在卡片顶部外沿,避开 name 区域 */
|
||
.hist-pl-corner { display: none; position: absolute; top: -7px; right: 6px; padding: 2px 6px; background: var(--color-price-lowest); color: #fff; font-size: 10px; font-weight: 600; border-radius: 4px; line-height: 1.4; z-index: 2; box-shadow: 0 1px 3px rgba(255,61,0,.25); }
|
||
.hist-pl-cell.lowest .hist-pl-corner { display: block; }
|
||
|
||
/* === 状态:未安装 平台 APP(用户没装该平台,引导下载) === */
|
||
.hist-pl-cell.not-installed { background: var(--color-surface-muted); border-style: dashed; }
|
||
.hist-pl-cell.not-installed .hist-pl-logo { opacity: .5; filter: grayscale(.3); }
|
||
.hist-pl-cell.not-installed .hist-pl-name { color: var(--color-ink-subtle); }
|
||
|
||
/* === 状态:此店在该平台不存在 === */
|
||
.hist-pl-cell.no-store { background: var(--color-surface-muted); }
|
||
.hist-pl-cell.no-store .hist-pl-logo { opacity: .35; filter: grayscale(1); }
|
||
.hist-pl-cell.no-store .hist-pl-name { color: var(--color-ink-subtle); }
|
||
|
||
/* 状态文字,替代 price 显示 */
|
||
.hist-pl-status { font-size: 12px; font-weight: 600; color: var(--color-ink-muted); line-height: 1; display: inline-flex; align-items: center; gap: 3px; }
|
||
.hist-pl-cell.not-installed { cursor: pointer; }
|
||
.hist-pl-cell.not-installed:active { background: #F0F0F0; }
|
||
.hist-pl-cell.not-installed .hist-pl-status { color: var(--color-ink-secondary); }
|
||
.hist-pl-cell.not-installed .hist-pl-status .dl-ico { display: inline-flex; align-items: center; justify-content: center; width: 12px; height: 12px; color: var(--color-primary-dark); margin-left: 1px; }
|
||
.hist-pl-cell.no-store .hist-pl-status { color: var(--color-ink-subtle); font-weight: 400; font-size: 11px; }
|
||
.hist-card-actions { display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin-top: 6px; min-height: 28px; }
|
||
.device.time-compact .hist-card-actions { justify-content: space-between; }
|
||
.hist-recompare-btn { height: 28px; padding: 0 12px; border-radius: 999px; border: 1px solid var(--color-primary); background: #FFFCE5; color: var(--color-ink); font-size: 12px; font-weight: 600; font-family: inherit; cursor: pointer; box-shadow: inset 0 1px 0 rgba(255,255,255,.7); }
|
||
.hist-recompare-btn:active { transform: scale(.96); background: #FFF7C2; }
|
||
.device.selecting .hist-card-actions { display: none; }
|
||
|
||
/* ============ 搜索态(点搜索图标进入) ============ */
|
||
/* 默认隐藏的搜索栏,搜索态时覆盖在 header 上 */
|
||
.hist-search-bar { display: none; position: absolute; inset: 0; padding: 0 16px 0 12px; align-items: center; gap: 10px; background: #F5F5F5; }
|
||
.device.searching .hist-search-bar { display: flex; }
|
||
.device.searching .hist-header-title { visibility: hidden; }
|
||
.device.searching .hist-header-search { display: none; }
|
||
|
||
.hist-search-input-wrap { flex: 1; display: flex; align-items: center; height: 34px; background: var(--color-surface-muted); border: 0.5px solid var(--color-divider-strong); border-radius: 17px; padding: 0 12px; gap: 6px; min-width: 0; }
|
||
.hist-search-input-wrap svg { flex-shrink: 0; color: var(--color-ink-subtle); }
|
||
.hist-search-input { flex: 1; border: 0; outline: 0; background: transparent; font-size: 14px; color: var(--color-ink); min-width: 0; padding: 0; }
|
||
.hist-search-input::placeholder { color: var(--color-ink-subtle); }
|
||
.hist-search-clear { width: 16px; height: 16px; border-radius: 50%; background: var(--color-ink-disabled); color: #fff; display: none; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; font-size: 11px; line-height: 1; }
|
||
.device.searching.has-input .hist-search-clear { display: flex; }
|
||
.hist-search-cancel { font-size: 15px; color: var(--color-ink); cursor: pointer; flex-shrink: 0; padding: 4px 0; }
|
||
.hist-search-cancel:active { opacity: .55; }
|
||
|
||
/* 搜索态:隐藏反馈 FAB 和底部 tab,让搜索结果占满 */
|
||
.device.searching .fb-fab { display: none; }
|
||
.device.searching .tab-bar { display: none; }
|
||
.device.searching .hist-scroll { bottom: 0; }
|
||
|
||
/* 最近搜索 chips —— 搜索态且无输入时显示 */
|
||
.hist-search-history { display: none; padding: 16px 0 8px; }
|
||
.device.searching:not(.has-input) .hist-search-history { display: block; }
|
||
.hist-search-history-title { font-size: 13px; font-weight: 600; color: var(--color-ink-secondary); margin-bottom: 12px; display: flex; align-items: center; justify-content: space-between; }
|
||
.hist-search-history-clear { font-size: 12px; color: var(--color-ink-subtle); font-weight: 400; cursor: pointer; }
|
||
.hist-search-history-clear:active { opacity: .55; }
|
||
.hist-search-history-chips { display: flex; flex-wrap: wrap; gap: 8px; }
|
||
.hist-search-chip { height: 28px; padding: 0 12px; background: var(--color-surface-muted); border-radius: 14px; display: inline-flex; align-items: center; font-size: 13px; color: var(--color-ink-secondary); cursor: pointer; }
|
||
.hist-search-chip:active { background: var(--color-divider); }
|
||
.hist-search-history-empty { font-size: 13px; color: var(--color-ink-subtle); padding: 4px 0; }
|
||
|
||
/* 列表条目搜索过滤 + 无结果空态 */
|
||
.device.searching .hist-item.search-hide { display: none; }
|
||
/* 搜索态但还没输入:完全不显示原列表(只露最近搜索 chips) */
|
||
.device.searching:not(.has-input) .hist-item { display: none; }
|
||
.hist-empty { display: none; padding: 80px 24px; text-align: center; color: var(--color-ink-subtle); font-size: 14px; line-height: 1.6; }
|
||
.device.searching.no-results .hist-empty { display: block; }
|
||
.device.searching:not(.has-input) .hist-empty { display: none; }
|
||
/* 命中关键词高亮 */
|
||
.search-hl { color: var(--color-price-lowest); font-weight: 700; }
|
||
|
||
/* 反馈低价相关样式集中在 style 末尾,本节预留 */
|
||
|
||
/* ======= Tab bar — 4 tab + 中央 AI 领券 FAB ======= */
|
||
/* 【隐藏底栏】WebView 里 App 有原生底栏,原型这个 H5 底栏整体隐藏 */
|
||
.tab-bar { display: none; }
|
||
.tab-item { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; cursor: pointer; position: relative; z-index: 1; }
|
||
.tab-icon { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; }
|
||
.tab-label { font-size: 11px; color: var(--color-ink-secondary); font-weight: 400; }
|
||
.tab-item.active .tab-label { color: var(--color-ink); font-weight: 700; }
|
||
.tab-icon .i-outline { display: block; }
|
||
.tab-icon .i-filled { display: none; }
|
||
.tab-item.active .tab-icon .i-outline { display: none; }
|
||
.tab-item.active .tab-icon .i-filled { display: block; }
|
||
.welfare-tab-dot {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 9px;
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
background: #FF3B30;
|
||
border: 1px solid #FFFFFF;
|
||
box-shadow: 0 2px 5px rgba(255, 59, 48, .32);
|
||
transform: translateX(7px);
|
||
display: none;
|
||
pointer-events: none;
|
||
z-index: 3;
|
||
}
|
||
.tab-item.has-welfare-dot .welfare-tab-dot { display: block; }
|
||
|
||
/* ======= 转场动画(与其他模块一致) ======= */
|
||
@keyframes nav-push-out { from { transform: translateX(0); opacity: 1; } to { transform: translateX(-30%); opacity: .7; } }
|
||
@keyframes nav-pop-out { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }
|
||
@keyframes nav-modal-out { from { transform: translateY(0); opacity: 1; } to { transform: translateY(100%); opacity: 0; } }
|
||
@keyframes nav-replace-out { from { opacity: 1; } to { opacity: 0; } }
|
||
@keyframes nav-fade-out { from { opacity: 1; } to { opacity: 0; } }
|
||
body.transitioning-push { animation: nav-push-out 280ms ease-in forwards; }
|
||
body.transitioning-pop { animation: nav-pop-out 280ms ease-in forwards; }
|
||
body.transitioning-modal { animation: nav-modal-out 280ms ease-in forwards; }
|
||
body.transitioning-replace { animation: nav-replace-out 200ms ease-out forwards; }
|
||
body.transitioning-fade { animation: nav-fade-out 400ms ease-in-out forwards; }
|
||
body.transitioning-tab { /* no animation */ }
|
||
|
||
/* ============ 未登录态:骨架模糊 + 居中登录卡(保留 tab / 上报 / 搜索骨架,点任意元素引导登录,不展示任何真实数据) ============ */
|
||
/* 骨架占位列表(模糊处理,不是真实记录)—— 【去框】bottom 60→0(无原生 tab-bar 占位) */
|
||
.hist-logout-skeleton { display: none; position: absolute; top: 88px; left: 0; right: 0; bottom: 0; overflow: hidden; padding: 8px 12px 24px; filter: blur(3px); pointer-events: none; }
|
||
.device.logged-out .hist-logout-skeleton { display: block; }
|
||
.hist-sk-item { background: #FFFFFF; border-radius: 14px; padding: 14px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,.04); }
|
||
.hist-sk-line { height: 12px; border-radius: 6px; background: linear-gradient(90deg,#ECECEC 0%,#F4F4F4 50%,#ECECEC 100%); }
|
||
.hist-sk-line.w60 { width: 60%; }
|
||
.hist-sk-line.w50 { width: 50%; }
|
||
.hist-sk-line.w30 { width: 30%; }
|
||
.hist-sk-line.sm { height: 9px; margin-top: 8px; }
|
||
.hist-sk-cells { display: grid; grid-template-columns: repeat(3,1fr); gap: 8px; margin-top: 14px; }
|
||
.hist-sk-cell { height: 46px; border-radius: 10px; background: linear-gradient(90deg,#EEE 0%,#F6F6F6 50%,#EEE 100%); border: .5px solid #ECECEC; }
|
||
/* 半透明蒙层,压住骨架,提示"锁定" —— 【去框】bottom 60→0 */
|
||
.hist-logout-scrim { display: none; position: absolute; top: 88px; left: 0; right: 0; bottom: 0; background: rgba(245,245,245,.45); z-index: 6; cursor: pointer; }
|
||
.device.logged-out .hist-logout-scrim { display: block; }
|
||
/* 居中登录卡容器:本身不拦点击(让 tab / 上报 / 骨架区的点击穿透到下层引导登录),只有卡片可点 —— 【去框】bottom 60→0 */
|
||
.hist-empty-logout { display: none; position: absolute; top: 44px; left: 0; right: 0; bottom: 0; align-items: center; justify-content: center; padding: 0 24px; z-index: 8; pointer-events: none; }
|
||
.hist-login-card { pointer-events: auto; }
|
||
.device.logged-out .hist-empty-logout { display: flex; }
|
||
.device.logged-out .hist-scroll { display: none; }
|
||
/* 从「我的」深链进入(无底部 tab bar)时,骨架/蒙层铺到底 */
|
||
.device.profile-entry.logged-out .hist-logout-skeleton,
|
||
.device.profile-entry.logged-out .hist-logout-scrim { bottom: 0; }
|
||
/* 登录卡本体 */
|
||
.hist-login-card { width: 248px; background: #FFFFFF; border-radius: 18px; padding: 24px 22px 22px; text-align: center; box-shadow: 0 12px 40px rgba(0,0,0,.16); }
|
||
.hist-login-card .lk { width: 60px; height: 60px; border-radius: 50%; background: #FFFDE7; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; border: 1.5px solid #FFE066; }
|
||
.hist-login-card .lk svg { width: 28px; height: 28px; }
|
||
.hist-login-card .empty-title { font-size: 16px; font-weight: 700; color: var(--color-ink); margin-bottom: 18px; line-height: 1.3; }
|
||
.hist-login-card .empty-cta { height: 40px; padding: 0 30px; background: var(--color-primary); color: var(--color-on-primary); border: 0; border-radius: 999px; font-size: 14px; font-weight: 700; box-shadow: 0 4px 12px rgba(255,179,0,.4), inset 0 1px 0 rgba(255,255,255,.6); cursor: pointer; transition: transform .12s; font-family: inherit; }
|
||
.hist-login-card .empty-cta:active { transform: scale(.96); }
|
||
|
||
/* AI 领券 toast 反馈(点击中央 FAB 时) */
|
||
.toast { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 14px 22px; background: rgba(0,0,0,.78); color: #fff; font-size: 15px; font-weight: 500; border-radius: 12px; opacity: 0; pointer-events: none; transition: opacity .2s; z-index: 100; max-width: 75%; text-align: center; }
|
||
.toast.show { opacity: 1; }
|
||
|
||
/* ======================================== */
|
||
/* 反馈低价 · 6 套 FAB 样式 + 单选模式 + 反馈抽屉 */
|
||
/* ======================================== */
|
||
|
||
/* —— 反馈低价 CTA —— 顶部右侧,位于搜索图标下方 */
|
||
.fb-fab { position: absolute; top: 96px; right: 16px; bottom: auto; height: 30px; padding: 0 12px; background: #FFFCE5; color: var(--color-on-primary); border-radius: 999px; font-size: 12px; font-weight: 700; font-family: inherit; display: flex; align-items: center; justify-content: center; border: 1px solid var(--color-primary); cursor: pointer; z-index: 4; transition: transform .14s ease, background .14s ease; white-space: nowrap; }
|
||
.fb-fab:active { transform: scale(.96); }
|
||
.fb-fab-badge { display: none; }
|
||
.device.cta-outline .fb-fab { background: #FFFFFF; border-color: #FFD600; box-shadow: none; }
|
||
.device.cta-solid .fb-fab { background: var(--color-primary); border-color: var(--color-primary); box-shadow: inset 0 1px 0 rgba(255,255,255,.65), 0 2px 6px rgba(255,179,0,.22); }
|
||
.device.cta-reward .fb-fab { height: 28px; padding: 0 10px; font-size: 11px; background: var(--color-primary); border-color: var(--color-primary); box-shadow: inset 0 1px 0 rgba(255,255,255,.55), 0 1px 4px rgba(255,179,0,.18); }
|
||
.device.cta-reward .fb-fab-badge { position: absolute; top: -7px; right: -4px; height: 14px; padding: 0 4px; background: #FF3D00; color: #fff; font-size: 8px; font-weight: 700; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; line-height: 1; box-shadow: 0 1px 3px rgba(255,61,0,.25); }
|
||
.device.selecting .fb-fab { display: none; }
|
||
|
||
/* —— 单选模式 —— */
|
||
.device.selecting .hist-header-search { display: none; }
|
||
.device.selecting .hist-header-title { font-size: 0; }
|
||
.device.selecting .hist-header-title::before { content: '选择一条记录上报'; font-size: 17px; font-weight: 600; color: var(--color-ink); }
|
||
.select-task-panel { display: none; position: absolute; left: 0; right: 0; top: 44px; padding: 8px 16px 6px; background: #F5F5F5; z-index: 2; }
|
||
.select-task-panel .fb-task-card { margin: 0; }
|
||
.device.selecting .select-task-panel { display: block; }
|
||
/* 【去框】单选态滚动区 bottom 76→0(action bar 改为 fixed 浮在视口底) */
|
||
.device.selecting .hist-scroll { top: 198px; bottom: 0; padding-bottom: 92px; }
|
||
|
||
/* 单选模式顶部 3 步指引 */
|
||
.device.selecting .tab-bar { display: none; }
|
||
/* 单选时整张卡片左侧让出 radio 空间(14 padding + 22 radio + 10 gap = 46) */
|
||
.device.selecting .hist-item { padding-left: 46px; cursor: pointer; }
|
||
|
||
.hist-item-radio { position: absolute; top: 14px; left: 14px; width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--color-divider-strong); background: var(--color-surface-card); display: none; align-items: center; justify-content: center; transition: all .15s; z-index: 1; }
|
||
.device.selecting .hist-item-radio { display: flex; }
|
||
.hist-item.selected { background: #FFFCE5; box-shadow: 0 0 0 1.5px var(--color-primary), 0 1px 3px rgba(0,0,0,.04); }
|
||
.hist-item.selected .hist-item-radio { background: var(--color-primary); border-color: var(--color-primary); }
|
||
.hist-item.selected .hist-item-radio::after { content: ''; position: absolute; left: 50%; top: 47%; width: 9px; height: 5px; border-left: 2px solid var(--color-on-primary); border-bottom: 2px solid var(--color-on-primary); background: transparent; border-radius: 0; transform: translate(-50%, -50%) rotate(-45deg); }
|
||
|
||
/* 单选模式底部 action bar —— 【去框】absolute→fixed 贴视口底 */
|
||
.fb-actionbar { position: fixed; left: 0; right: 0; bottom: 0; height: 76px; display: none; flex-direction: row; align-items: center; gap: 10px; padding: 12px 16px 16px; background: rgba(255,255,255,.96); border-top: .5px solid var(--color-divider-strong); z-index: 7; backdrop-filter: saturate(180%) blur(20px); -webkit-backdrop-filter: saturate(180%) blur(20px); }
|
||
.device.selecting .fb-actionbar { display: flex; }
|
||
.fb-next-btn { flex: 1; height: 44px; border-radius: 999px; border: 0; background: var(--color-primary); color: var(--color-on-primary); font-size: 15px; font-weight: 700; box-shadow: var(--shadow-cta-warm); cursor: pointer; transition: transform .12s; }
|
||
.fb-next-btn:active { transform: scale(.98); }
|
||
.fb-next-btn:disabled { background: var(--color-ink-disabled); color: #fff; box-shadow: none; cursor: not-allowed; }
|
||
|
||
/* —— 反馈 bottom-sheet —— */
|
||
/* 反馈低价独立页面(push 从右进入,全屏)—— 【去框】absolute→fixed,top 44→0 铺满 WebView */
|
||
.fb-mask { display: none; }
|
||
.fb-sheet { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: #F5F5F5; z-index: 11; transform: translateX(100%); transition: transform .3s cubic-bezier(.2,.8,.2,1); display: flex; flex-direction: column; }
|
||
.fb-sheet.show { transform: translateX(0); }
|
||
|
||
/* 页面顶部 header */
|
||
.fb-page-header { height: 44px; padding: 0 12px; background: #F5F5F5; display: flex; align-items: center; position: relative; flex-shrink: 0; }
|
||
.fb-page-back { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
|
||
.fb-page-back:active { opacity: .5; }
|
||
.fb-page-title { position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); text-align: center; font-size: 17px; font-weight: 600; color: var(--color-ink); pointer-events: none; }
|
||
.fb-page-reports { margin-left: auto; display: inline-flex; align-items: center; gap: 4px; padding: 6px 4px; font-size: 14px; color: var(--color-ink-muted); font-weight: 400; background: none; border: none; cursor: pointer; font-family: inherit; }
|
||
.fb-page-reports:active { opacity: .5; }
|
||
.fb-page-reports .fb-page-reports-dot { width: 5px; height: 5px; border-radius: 50%; background: #FF3D00; flex-shrink: 0; align-self: flex-start; margin-top: 4px; }
|
||
|
||
/* 内容滚动区 */
|
||
.fb-page-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 0 16px 16px; min-height: 0; overscroll-behavior: contain; }
|
||
.fb-page-body::-webkit-scrollbar { display: none; }
|
||
|
||
/* 底部固定提交按钮容器 */
|
||
.fb-page-footer { flex-shrink: 0; padding: 12px 16px calc(20px + env(safe-area-inset-bottom, 0)); background: #FFFFFF; border-top: .5px solid var(--color-divider); }
|
||
.fb-sheet-title { font-size: 16px; font-weight: 600; color: var(--color-ink); text-align: center; line-height: 1.3; }
|
||
|
||
/* 顶部奖励横幅(取代原 sheet-title 位置,承担「身份 + 利益」双重作用) */
|
||
.fb-reward-banner-top { display: block; text-align: center; background: linear-gradient(135deg, #FFF1A8 0%, #FFE066 100%); border: 1px solid #FFD600; border-radius: 12px; padding: 12px 16px; margin: 0 0 18px; font-size: 14px; font-weight: 700; color: #5A3A00; line-height: 1.3; box-shadow: inset 0 1px 0 rgba(255,255,255,.55), 0 1px 2px rgba(255,179,0,.18); }
|
||
.fb-reward-banner-top .num { font-family: var(--font-num); font-size: 17px; font-weight: 800; color: var(--color-price-lowest); margin: 0 3px 0 2px; }
|
||
.fb-reward-banner-top img { width: 20px; height: 20px; margin: 0 2px; vertical-align: -6px; }
|
||
|
||
/* 顶部 4 步任务卡(仿识货) */
|
||
.fb-task-card { background: var(--color-surface-card); border: 1px solid var(--color-divider); border-radius: 14px; padding: 14px 6px; margin: 12px 0 16px; display: flex; align-items: flex-start; justify-content: space-between; gap: 2px; }
|
||
.fb-task-step { display: flex; flex-direction: column; align-items: center; gap: 6px; flex: 1; min-width: 0; }
|
||
.fb-task-icon { width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; color: var(--color-ink); }
|
||
.fb-task-text { font-size: 11px; color: var(--color-ink); font-weight: 500; text-align: center; line-height: 1.3; padding: 0 2px; }
|
||
.fb-task-text .line { display: block; white-space: nowrap; }
|
||
.fb-task-text .hl { color: var(--color-price-lowest); font-weight: 800; font-family: var(--font-num); }
|
||
.fb-task-arrow { color: var(--color-ink-muted); font-size: 14px; margin-top: 7px; flex-shrink: 0; font-weight: 400; }
|
||
/* 第 4 步「奖励」整体高亮为橙红色 */
|
||
.fb-task-step.reward .fb-task-icon svg { stroke: var(--color-ink); }
|
||
.fb-task-step.reward .fb-task-text { color: var(--color-ink); font-weight: 700; }
|
||
.fb-task-step.active .fb-task-icon svg { stroke: var(--color-price-lowest); }
|
||
.fb-task-step.active .fb-task-text { color: var(--color-price-lowest); font-weight: 700; }
|
||
.fb-sheet-sub { font-size: 12px; color: var(--color-ink-muted); text-align: center; margin-top: 4px; margin-bottom: 16px; }
|
||
.fb-sheet-sub .gold { color: var(--color-price-lowest); font-weight: 700; }
|
||
|
||
/* 奖励横幅 —— 强调「审核通过 = 1000 金币」这个核心信息 */
|
||
.fb-reward-banner { display: flex; align-items: center; justify-content: center; gap: 4px; background: linear-gradient(135deg, #FFF9DB 0%, #FFF3C4 100%); border: 1px solid #FFE066; border-radius: 12px; padding: 10px 14px; margin: 12px 0 16px; font-size: 13px; font-weight: 600; color: #5A3A00; line-height: 1.3; }
|
||
.fb-coin-icon { width: 18px; height: 18px; vertical-align: -4px; margin: 0 2px 0 4px; }
|
||
.fb-reward-banner .num { font-family: var(--font-num); font-size: 15px; font-weight: 800; color: var(--color-price-lowest); margin-right: 2px; }
|
||
|
||
.fb-target-card { background: var(--color-surface-card); border: 1px solid var(--color-divider); border-radius: 14px; padding: 14px; margin-bottom: 18px; }
|
||
.fb-target-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
|
||
.fb-target-store { font-size: 15px; font-weight: 600; color: var(--color-ink); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.3; }
|
||
.fb-target-date { font-size: 12px; color: var(--color-ink-subtle); flex-shrink: 0; }
|
||
.fb-target-dish { font-size: 13px; color: var(--color-ink-secondary); margin-top: 6px; line-height: 1.4; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
||
.fb-target-dish.is-fold { position: relative; display: flex; align-items: center; overflow: visible; text-overflow: clip; white-space: normal; padding-right: 18px; cursor: pointer; user-select: none; min-width: 0; }
|
||
.fb-target-dish.is-fold:active { opacity: .55; }
|
||
.fb-target-dish-names { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.4; }
|
||
.fb-target-dish-count { margin-left: 4px; color: var(--color-ink-subtle); flex-shrink: 0; line-height: 1.4; }
|
||
.fb-target-dish-arrow { position: absolute; right: 0; top: 50%; width: 12px; height: 12px; color: var(--color-ink-disabled); display: flex; align-items: center; justify-content: center; transform: translateY(-50%); transition: transform .16s ease, color .16s ease; transform-origin: center; }
|
||
.fb-target-dish-arrow svg { width: 12px; height: 12px; display: block; stroke: currentColor; overflow: visible; }
|
||
.fb-target-dish.is-fold.expanded { align-items: flex-start; }
|
||
.fb-target-dish.is-fold.expanded .fb-target-dish-names { white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.45; }
|
||
.fb-target-dish.is-fold.expanded .fb-target-dish-count { padding-top: 1px; }
|
||
.fb-target-dish.is-fold.expanded .fb-target-dish-arrow { top: 3px; color: var(--color-ink-subtle); transform: rotate(180deg); }
|
||
.fb-target-lowest { display: flex; align-items: center; height: 22px; margin-top: 12px; padding-top: 12px; border-top: 0.5px solid var(--color-divider); }
|
||
.fb-target-pl-logo { width: 16px; height: 16px; border-radius: 3px; object-fit: cover; margin-right: 8px; flex-shrink: 0; }
|
||
.fb-target-pl-name { font-size: 14px; color: var(--color-ink); font-weight: 600; flex: 1; min-width: 0; }
|
||
.fb-target-pl-tag { height: 16px; padding: 0 5px; border-radius: 3px; background: var(--color-price-lowest-bg); color: var(--color-price-lowest); font-size: 10px; font-weight: 700; display: inline-flex; align-items: center; margin-right: 6px; }
|
||
.fb-target-pl-price { font-family: var(--font-num); font-size: 16px; font-weight: 700; color: var(--color-price-lowest); line-height: 1; }
|
||
|
||
.fb-field { margin-bottom: 16px; }
|
||
.fb-field-label { font-size: 13px; font-weight: 600; color: var(--color-ink); margin-bottom: 8px; }
|
||
.fb-field-label .req { color: var(--color-price-lowest); margin-left: 2px; }
|
||
.fb-field-hint { font-size: 12px; color: var(--color-ink-subtle); line-height: 1.4; margin: -4px 0 10px; }
|
||
|
||
.fb-price-input-row { display: flex; align-items: center; gap: 6px; padding: 12px 14px; border: 1px solid var(--color-divider-strong); border-radius: 14px; background: var(--color-surface-card); }
|
||
.fb-price-input-row:focus-within { border-color: var(--color-primary); }
|
||
.fb-price-input-prefix { font-size: 17px; font-weight: 700; color: var(--color-price-lowest); font-family: var(--font-num); }
|
||
.fb-price-input { flex: 1; border: 0; outline: 0; font-size: 17px; font-family: var(--font-num); font-weight: 700; color: var(--color-price-lowest); background: transparent; width: 100%; }
|
||
.fb-price-input::placeholder { color: var(--color-ink-disabled); font-weight: 400; }
|
||
|
||
.fb-platform-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
|
||
.fb-platform-chip { height: 36px; border-radius: 999px; border: 1px solid var(--color-divider-strong); background: var(--color-surface-card); display: inline-flex; align-items: center; justify-content: center; gap: 5px; font-size: 13px; font-weight: 600; color: var(--color-ink-secondary); cursor: pointer; transition: all .15s; }
|
||
.fb-platform-chip img { width: 14px; height: 14px; border-radius: 3px; flex-shrink: 0; }
|
||
.fb-platform-chip.active { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-primary); }
|
||
|
||
|
||
.fb-upload-row { display: flex; gap: 8px; }
|
||
.fb-upload-slot { width: 80px; height: 80px; border-radius: 10px; background: #F2F2F4; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; color: var(--color-ink-muted); font-size: 12px; cursor: pointer; transition: all .15s; position: relative; }
|
||
.fb-upload-slot:active { transform: scale(.96); }
|
||
.fb-upload-plus { width: 22px; height: 22px; position: relative; }
|
||
.fb-upload-plus::before, .fb-upload-plus::after { content: ''; position: absolute; background: var(--color-ink-muted); border-radius: 1.5px; }
|
||
.fb-upload-plus::before { left: 50%; top: 0; width: 2px; height: 22px; transform: translateX(-50%); }
|
||
.fb-upload-plus::after { top: 50%; left: 0; width: 22px; height: 2px; transform: translateY(-50%); }
|
||
.fb-upload-slot.uploaded { background: var(--color-price-lowest-bg); color: var(--color-price-lowest); }
|
||
.fb-upload-slot.uploaded .fb-upload-plus::before, .fb-upload-slot.uploaded .fb-upload-plus::after { background: var(--color-price-lowest); }
|
||
|
||
/* 示例截图 tile —— 放在上传槽左侧,引导用户对照上传 */
|
||
.fb-sample-slot { width: 80px; height: 80px; border-radius: 10px; background: linear-gradient(180deg, #FFF8E1 0%, #FFFDF5 100%); border: 1px solid #FFE082; position: relative; cursor: pointer; overflow: hidden; display: flex; flex-direction: column; transition: transform .15s ease; flex-shrink: 0; }
|
||
.fb-sample-slot:active { transform: scale(.96); }
|
||
.fb-sample-badge { position: absolute; top: 0; left: 0; background: #1A1A1A; color: var(--color-primary); font-size: 10px; font-weight: 700; line-height: 1; padding: 3px 6px 3px 5px; border-radius: 10px 0 8px 0; letter-spacing: .04em; z-index: 2; }
|
||
.fb-sample-zoom { position: absolute; top: 4px; right: 4px; width: 18px; height: 18px; border-radius: 50%; background: rgba(26,26,26,.7); display: flex; align-items: center; justify-content: center; z-index: 2; }
|
||
.fb-sample-zoom svg { width: 10px; height: 10px; }
|
||
.fb-sample-thumb { flex: 1; padding: 4px 6px 6px; display: flex; flex-direction: column; }
|
||
.fb-sample-thumb-bar { height: 8px; background: var(--color-platform-meituan-waimai); border-radius: 2px; margin-top: 12px; margin-bottom: 4px; }
|
||
.fb-sample-thumb-line { height: 4px; background: #E5E5E5; border-radius: 2px; margin-bottom: 3px; }
|
||
.fb-sample-thumb-line.s { width: 60%; }
|
||
.fb-sample-thumb-line.m { width: 80%; }
|
||
.fb-sample-thumb-price { font-family: var(--font-num); font-size: 14px; font-weight: 700; color: var(--color-price-lowest); line-height: 1; margin-top: 4px; letter-spacing: -.5px; }
|
||
.fb-sample-thumb-chip { display: inline-flex; align-items: center; background: var(--color-price-lowest-bg); color: var(--color-price-lowest); font-size: 8px; font-weight: 700; padding: 1px 3px; border-radius: 3px; margin-top: 3px; align-self: flex-start; line-height: 1.2; }
|
||
|
||
/* 上传字段下方提示 */
|
||
.fb-upload-callout { display: flex; gap: 6px; align-items: center; font-size: 11px; color: var(--color-ink-muted); margin-top: 10px; }
|
||
.fb-upload-callout svg { width: 12px; height: 12px; flex-shrink: 0; color: var(--color-primary-dark); }
|
||
.fb-field-hint .em { color: var(--color-price-lowest); font-weight: 600; }
|
||
|
||
/* 示例放大查看器 modal —— 【去框】absolute inset:0 在 fixed 的 fb-sheet 内仍贴满,无需改 */
|
||
.fb-sample-modal { position: absolute; inset: 0; background: rgba(26,26,26,.88); display: none; flex-direction: column; z-index: 20; -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
|
||
.fb-sample-modal.show { display: flex; }
|
||
.fb-sample-modal-header { height: 56px; display: flex; align-items: center; justify-content: space-between; padding: 0 16px; color: #fff; flex-shrink: 0; }
|
||
.fb-sample-modal-title { font-size: 15px; font-weight: 600; }
|
||
.fb-sample-modal-close { width: 32px; height: 32px; border-radius: 50%; background: rgba(255,255,255,.15); display: flex; align-items: center; justify-content: center; cursor: pointer; }
|
||
.fb-sample-modal-close svg { width: 14px; height: 14px; }
|
||
.fb-sample-modal-body { flex: 1; padding: 8px 24px 24px; display: flex; flex-direction: column; gap: 16px; align-items: center; justify-content: flex-start; overflow-y: auto; }
|
||
.fb-sample-fullimg { width: 240px; border-radius: 14px; background: #fff; overflow: hidden; position: relative; box-shadow: 0 8px 24px rgba(0,0,0,.4); flex-shrink: 0; }
|
||
.fb-sample-fullimg .fi-header { height: 36px; background: var(--color-platform-meituan-waimai); display: flex; align-items: center; padding: 0 12px; font-size: 12px; font-weight: 700; color: var(--color-ink); }
|
||
.fb-sample-fullimg .fi-header img { width: 16px; height: 16px; border-radius: 3px; margin-right: 6px; }
|
||
.fb-sample-fullimg .fi-block { padding: 12px 12px 8px; border-bottom: 1px solid var(--color-divider); }
|
||
.fb-sample-fullimg .fi-shop { font-size: 13px; font-weight: 600; color: var(--color-ink); margin-bottom: 8px; }
|
||
.fb-sample-fullimg .fi-row { display: flex; justify-content: space-between; font-size: 12px; color: var(--color-ink-secondary); margin-bottom: 4px; }
|
||
.fb-sample-fullimg .fi-row .name { flex: 1; padding-right: 12px; }
|
||
.fb-sample-fullimg .fi-row .price { font-family: var(--font-num); color: var(--color-ink); }
|
||
.fb-sample-fullimg .fi-disc { display: flex; justify-content: space-between; font-size: 11px; color: var(--color-price-lowest); margin-top: 6px; }
|
||
.fb-sample-fullimg .fi-disc .price { font-family: var(--font-num); }
|
||
.fb-sample-fullimg .fi-total { padding: 12px; display: flex; justify-content: space-between; align-items: baseline; background: var(--color-surface-page); }
|
||
.fb-sample-fullimg .fi-total .lbl { font-size: 12px; color: var(--color-ink-muted); }
|
||
.fb-sample-fullimg .fi-total .val { font-family: var(--font-num); font-size: 22px; font-weight: 700; color: var(--color-price-lowest); }
|
||
.fb-sample-annot { position: absolute; border: 2px solid var(--color-primary); border-radius: 6px; pointer-events: none; box-shadow: 0 0 0 2px rgba(255,214,0,.2); }
|
||
.fb-sample-annot .num { position: absolute; top: -10px; right: -10px; width: 20px; height: 20px; background: var(--color-primary); color: var(--color-ink); border-radius: 50%; font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; line-height: 1; }
|
||
.fb-sample-annot.a1 { top: 36px; left: 8px; right: 8px; height: 22px; }
|
||
.fb-sample-annot.a2 { bottom: 60px; left: 8px; right: 8px; height: 18px; }
|
||
.fb-sample-annot.a3 { bottom: 8px; left: 8px; right: 8px; height: 38px; }
|
||
.fb-sample-rules { width: 100%; max-width: 280px; background: rgba(255,255,255,.08); border-radius: 12px; padding: 12px 14px; color: #fff; flex-shrink: 0; }
|
||
.fb-sample-rules-title { font-size: 12px; font-weight: 700; color: var(--color-primary); margin-bottom: 8px; letter-spacing: .04em; }
|
||
.fb-sample-rules-item { font-size: 12px; line-height: 1.5; color: rgba(255,255,255,.92); display: flex; gap: 6px; margin-bottom: 4px; }
|
||
.fb-sample-rules-item .n { color: var(--color-primary); font-weight: 700; flex-shrink: 0; }
|
||
.fb-sample-rules-item .em { color: var(--color-primary); font-weight: 700; }
|
||
|
||
.fb-submit-btn { width: 100%; height: 48px; border-radius: 14px; border: 0; background: linear-gradient(180deg, #FFE066 0%, #FFD600 45%, #FFB300 100%); color: var(--color-on-primary); font-size: 17px; font-weight: 700; box-shadow: var(--shadow-cta-warm); cursor: pointer; transition: transform .12s; }
|
||
.fb-submit-btn:active { transform: scale(.98); }
|
||
.fb-submit-btn:disabled { background: var(--color-ink-disabled); box-shadow: none; cursor: not-allowed; color: #fff; }
|
||
|
||
/* —— 模拟 iOS 数字键盘(focus 价格输入框时升起)—— 【去框】absolute→fixed 贴视口底 */
|
||
.fb-keyboard { position: fixed; left: 0; right: 0; bottom: 0; height: 226px; background: #D1D5DB; padding: 6px 4px 8px; display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(4, 1fr); gap: 6px; z-index: 12; transform: translateY(100%); transition: transform .28s cubic-bezier(.2,.8,.2,1); }
|
||
.fb-keyboard.show { transform: translateY(0); }
|
||
.fb-key { background: #FFFFFF; border-radius: 5px; display: flex; align-items: center; justify-content: center; font-size: 25px; font-weight: 400; color: #1A1A1A; cursor: pointer; box-shadow: 0 1px 0 rgba(0,0,0,.28); user-select: none; -webkit-user-select: none; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
|
||
.fb-key:active { background: #BCC0C6; transition: background 0s; }
|
||
.fb-key.fn { background: #ABB1BB; font-size: 17px; }
|
||
.fb-key.fn svg { display: block; }
|
||
|
||
/* sheet 在键盘升起时收缩到键盘上方 */
|
||
/* 键盘升起时,整个 fb-sheet 向上偏移让出键盘空间 */
|
||
.device.kb-up .fb-sheet { bottom: 226px; }
|
||
|
||
</style>
|
||
<script src="../shared/bridge.js"></script>
|
||
</head>
|
||
<body>
|
||
<div class="device time-compact">
|
||
|
||
<!-- status bar(App 内有原生状态栏,已 display:none 隐藏,保留 DOM 防脚本引用报错) -->
|
||
<div class="status-bar">
|
||
<span>9:41</span>
|
||
<span class="right"></span>
|
||
</div>
|
||
|
||
<!-- 顶部筛选 tab(正常态显示,单选 / 搜索态自动隐藏) -->
|
||
<div class="hist-filter">
|
||
<div class="hist-filter-tab active" data-filter="all" onclick="onFilterClick(this)">全部记录</div>
|
||
<div class="hist-filter-tab" data-filter="ordered" onclick="onFilterClick(this)">已下单</div>
|
||
</div>
|
||
|
||
<!-- header:居中标题 + 右侧搜索;搜索态时被 .hist-search-bar 覆盖;单选态时左上「取消」 -->
|
||
<div class="hist-header">
|
||
<div class="hist-header-cancel" onclick="cancelSelect()" role="button">取消</div>
|
||
<button class="hist-header-back" type="button" onclick="goBackToProfile()" aria-label="返回我的页面">
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 6L9 12L15 18" stroke="#1A1A1A" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
</button>
|
||
<button class="hist-header-reports" type="button" onclick="goReports()" aria-label="查看上报记录">上报记录<span class="hist-header-reports-dot" style="display:none"></span></button>
|
||
<div class="hist-header-title">比价记录</div>
|
||
<div class="hist-header-search" onclick="enterSearchMode()" role="button" aria-label="搜索">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="2.2" stroke-linecap="round">
|
||
<circle cx="11" cy="11" r="7"></circle>
|
||
<path d="m20 20-3.5-3.5"></path>
|
||
</svg>
|
||
</div>
|
||
<!-- 搜索态 bar,默认 display:none,.device.searching 时显示 -->
|
||
<div class="hist-search-bar">
|
||
<div class="hist-search-input-wrap">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true">
|
||
<circle cx="11" cy="11" r="7"></circle>
|
||
<path d="m20 20-3.5-3.5"></path>
|
||
</svg>
|
||
<input type="text" class="hist-search-input" id="histSearchInput" placeholder="搜店名 / 菜名" oninput="onSearchInput(this.value)" autocomplete="off">
|
||
<span class="hist-search-clear" onclick="clearSearch()" aria-label="清空">✕</span>
|
||
</div>
|
||
<span class="hist-search-cancel" onclick="exitSearchMode()">取消</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="select-task-panel" aria-hidden="true">
|
||
<div class="fb-task-card">
|
||
<div class="fb-task-step active">
|
||
<div class="fb-task-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 8h8M8 12h8M8 16h5"/></svg>
|
||
</div>
|
||
<div class="fb-task-text"><span class="line">选择要</span><span class="line">上报的记录</span></div>
|
||
</div>
|
||
<span class="fb-task-arrow">›</span>
|
||
<div class="fb-task-step">
|
||
<div class="fb-task-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7.5l2.5-3.5h13L21 7.5v11.5a1.5 1.5 0 01-1.5 1.5h-15A1.5 1.5 0 013 19V7.5z"/><circle cx="12" cy="13" r="4"/></svg>
|
||
</div>
|
||
<div class="fb-task-text">补充低价<br>相关证明</div>
|
||
</div>
|
||
<span class="fb-task-arrow">›</span>
|
||
<div class="fb-task-step">
|
||
<div class="fb-task-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8" r="3.2"/><path d="M4.5 19c.5-3.2 2.4-5 4.8-5 1.1 0 2.1.3 2.8.9"/><path d="M14.5 17l2 2 4-4"/></svg>
|
||
</div>
|
||
<div class="fb-task-text">人工<br>审核</div>
|
||
</div>
|
||
<span class="fb-task-arrow">›</span>
|
||
<div class="fb-task-step reward">
|
||
<div class="fb-task-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M8.5 7.5l3.5 4 3.5-4"/><path d="M8 13h8"/><path d="M8 16h8"/><path d="M12 11.5v6.5"/></svg>
|
||
</div>
|
||
<div class="fb-task-text">得 1000<br>金币奖励</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 滚动区 —— 列表由 JS 动态渲染进 #histList,6 张写死卡片已删除 -->
|
||
<div class="hist-scroll">
|
||
|
||
<!-- 搜索态:最近搜索(仅 .device.searching 且无输入时显示) -->
|
||
<div class="hist-search-history">
|
||
<div class="hist-search-history-title">
|
||
<span>最近搜索</span>
|
||
<span class="hist-search-history-clear" onclick="clearSearchHistory()">清除</span>
|
||
</div>
|
||
<div class="hist-search-history-chips" id="histSearchChips"></div>
|
||
<div class="hist-search-history-empty" id="histSearchEmpty" style="display:none">暂无搜索记录</div>
|
||
</div>
|
||
|
||
<!-- 动态列表挂载点(renderList 往这里写 renderHistItem 生成的卡片) -->
|
||
<div id="histList"></div>
|
||
|
||
<!-- 搜索无结果空态 -->
|
||
<div class="hist-empty">没有找到匹配的比价记录<br>试试其他关键词</div>
|
||
|
||
</div>
|
||
|
||
<!-- tab bar(已 display:none 隐藏,保留 DOM 防脚本引用报错) -->
|
||
<div class="tab-bar">
|
||
<div class="tab-item" onclick="go('home')"><span class="tab-icon"><svg class="i-outline" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12l9-8 9 8"></path><path d="M5 10v9a1 1 0 001 1h3v-5h6v5h3a1 1 0 001-1v-9"></path></svg><svg class="i-filled" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M3 12l9-8 9 8" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M5 10v9a1 1 0 001 1h3v-5h6v5h3a1 1 0 001-1v-9" fill="#FFD600" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span><span class="tab-label">首页</span></div>
|
||
<div class="tab-item" id="homeTabWelfare" onclick="go('welfare')"><span class="welfare-tab-dot" aria-hidden="true"></span><span class="tab-icon" id="homeTabWelfareIcon"><svg class="i-outline" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="8" width="18" height="4" rx="1"></rect><path d="M12 8v12"></path><path d="M5 12v7a1 1 0 001 1h12a1 1 0 001-1v-7"></path><path d="M7.5 8C6 8 5 6.5 6 5.5S9 4 9.5 5.5c.4 1.2 2.5 2.5 2.5 2.5s2.1-1.3 2.5-2.5C15 4 17 4.5 18 5.5s-1 2.5-2.5 2.5"></path></svg><svg class="i-filled" width="24" height="24" viewBox="0 0 24 24" fill="none"><rect x="3" y="8" width="18" height="4" rx="1" fill="#FFD600" stroke="#1a1a1a" stroke-width="2"></rect><path d="M5 12v7a1 1 0 001 1h12a1 1 0 001-1v-7" fill="#FFD600" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 8v12" stroke="#1a1a1a" stroke-width="2"></path><path d="M7.5 8C6 8 5 6.5 6 5.5S9 4 9.5 5.5c.4 1.2 2.5 2.5 2.5 2.5s2.1-1.3 2.5-2.5C15 4 17 4.5 18 5.5s-1 2.5-2.5 2.5" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span><span class="tab-label">福利</span></div>
|
||
<div class="tab-item active" onclick="go('history')"><span class="tab-icon"><svg class="i-outline" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2.5"></rect><path d="M8 9h8"></path><path d="M8 13h8"></path><path d="M8 17h5"></path></svg><svg class="i-filled" width="24" height="24" viewBox="0 0 24 24" fill="none"><rect x="4" y="4" width="16" height="16" rx="2.5" fill="#FFD600" stroke="#1a1a1a" stroke-width="2"></rect><path d="M8 9h8" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round"></path><path d="M8 13h8" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round"></path><path d="M8 17h5" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round"></path></svg></span><span class="tab-label">记录</span></div>
|
||
<div class="tab-item" onclick="go('profile')"><span class="tab-icon"><svg class="i-outline" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"></circle><path d="M4 20c0-4 4-6 8-6s8 2 8 6"></path></svg><svg class="i-filled" width="24" height="24" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="8" r="4" fill="#FFD600" stroke="#1a1a1a" stroke-width="2"></circle><path d="M4 20c0-4 4-6 8-6s8 2 8 6" fill="#FFD600" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span><span class="tab-label">我的</span></div>
|
||
</div>
|
||
|
||
<!-- 上报更低价顶部 CTA -->
|
||
<button class="fb-fab" onclick="enterSelectMode()" aria-label="上报更低价赚金币">
|
||
上报更低价<span class="fb-fab-badge">赚金币</span>
|
||
</button>
|
||
|
||
<!-- 单选模式底部 action bar -->
|
||
<div class="fb-actionbar">
|
||
<button class="fb-next-btn" id="fbNextBtn" disabled onclick="openFeedbackSheet()">下一步</button>
|
||
</div>
|
||
|
||
<!-- 反馈低价 独立页面(push 从右进入) -->
|
||
<div class="fb-sheet" id="fbSheet" role="dialog">
|
||
<div class="fb-page-header">
|
||
<div class="fb-page-back" onclick="closeFeedbackSheet()" role="button" aria-label="返回">
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
|
||
</div>
|
||
<div class="fb-page-title">上报更低价</div>
|
||
<button class="fb-page-reports" type="button" onclick="goReports()" aria-label="查看上报记录">上报记录<span class="fb-page-reports-dot" style="display:none"></span></button>
|
||
</div>
|
||
|
||
<div class="fb-page-body">
|
||
<div class="fb-task-card">
|
||
<div class="fb-task-step">
|
||
<div class="fb-task-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 8h8M8 12h8M8 16h5"/></svg>
|
||
</div>
|
||
<div class="fb-task-text"><span class="line">选择要</span><span class="line">上报的记录</span></div>
|
||
</div>
|
||
<span class="fb-task-arrow">›</span>
|
||
<div class="fb-task-step active">
|
||
<div class="fb-task-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7.5l2.5-3.5h13L21 7.5v11.5a1.5 1.5 0 01-1.5 1.5h-15A1.5 1.5 0 013 19V7.5z"/><circle cx="12" cy="13" r="4"/></svg>
|
||
</div>
|
||
<div class="fb-task-text">补充低价<br>相关证明</div>
|
||
</div>
|
||
<span class="fb-task-arrow">›</span>
|
||
<div class="fb-task-step">
|
||
<div class="fb-task-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8" r="3.2"/><path d="M4.5 19c.5-3.2 2.4-5 4.8-5 1.1 0 2.1.3 2.8.9"/><path d="M14.5 17l2 2 4-4"/></svg>
|
||
</div>
|
||
<div class="fb-task-text">人工<br>审核</div>
|
||
</div>
|
||
<span class="fb-task-arrow">›</span>
|
||
<div class="fb-task-step reward">
|
||
<div class="fb-task-icon">
|
||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M8.5 7.5l3.5 4 3.5-4"/><path d="M8 13h8"/><path d="M8 16h8"/><path d="M12 11.5v6.5"/></svg>
|
||
</div>
|
||
<div class="fb-task-text">得 1000<br>金币奖励</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="fb-target-card">
|
||
<div class="fb-target-head">
|
||
<div class="fb-target-store" id="fbTargetStore"></div>
|
||
<div class="fb-target-date" id="fbTargetDate"></div>
|
||
</div>
|
||
<div class="fb-target-dish" id="fbTargetDish"></div>
|
||
<div class="fb-target-lowest">
|
||
<img class="fb-target-pl-logo" id="fbTargetPlLogo" src="" alt="">
|
||
<span class="fb-target-pl-name" id="fbTargetPlatform"></span>
|
||
<span class="fb-target-pl-tag">原最低价</span>
|
||
<span class="fb-target-pl-price" id="fbTargetPrice"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="fb-field">
|
||
<div class="fb-field-label">填写您看到的更低价<span class="req">*</span></div>
|
||
<div class="fb-price-input-row">
|
||
<span class="fb-price-input-prefix">¥</span>
|
||
<input type="text" inputmode="decimal" autocomplete="off" class="fb-price-input" id="fbPriceInput" placeholder="0.00" readonly oninput="refreshSubmitBtn()">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="fb-field">
|
||
<div class="fb-field-label">您是在哪里找到更低价的<span class="req">*</span></div>
|
||
<div class="fb-platform-grid">
|
||
<div class="fb-platform-chip" data-pf="meituan-waimai"><img src="logos/meituan.png" alt="">美团</div>
|
||
<div class="fb-platform-chip" data-pf="jd-waimai"><img src="logos/jd.png" alt="">京东</div>
|
||
<div class="fb-platform-chip" data-pf="taobao-shanguang"><img src="logos/taobao.png" alt="">淘宝</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="fb-field">
|
||
<div class="fb-field-label">上传截图证明<span class="req">*</span></div>
|
||
<div class="fb-field-hint">需展示<span class="em">使用红包后的实付价</span>,方便审核通过</div>
|
||
<div class="fb-upload-row">
|
||
<div class="fb-sample-slot" onclick="openSampleModal()" role="button" aria-label="查看截图示例">
|
||
<div class="fb-sample-badge">示例</div>
|
||
<div class="fb-sample-zoom" aria-hidden="true">
|
||
<svg viewBox="0 0 10 10" fill="none"><circle cx="4" cy="4" r="3" stroke="#fff" stroke-width="1.2"/><path d="M6.5 6.5L9 9" stroke="#fff" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||
</div>
|
||
<div class="fb-sample-thumb">
|
||
<div class="fb-sample-thumb-bar"></div>
|
||
<div class="fb-sample-thumb-line m"></div>
|
||
<div class="fb-sample-thumb-line s"></div>
|
||
<div class="fb-sample-thumb-price">¥12.80</div>
|
||
<div class="fb-sample-thumb-chip">已抵 ¥5</div>
|
||
</div>
|
||
</div>
|
||
<div class="fb-upload-slot" id="fbUploadSlot" onclick="onPickPhoto()">
|
||
<span class="fb-upload-plus" aria-hidden="true"></span>
|
||
<span class="fb-upload-text">点击添加</span>
|
||
</div>
|
||
<!-- 隐藏文件选择 input:点上传槽 → 触发它 → 原生 onShowFileChooser 弹系统相册多选(≤4 张) -->
|
||
<input type="file" accept="image/*" multiple id="fbFileInput" style="display:none" onchange="onPhotoFilesPicked(this)">
|
||
</div>
|
||
<div class="fb-upload-callout">
|
||
<svg viewBox="0 0 12 12" fill="none"><circle cx="6" cy="6" r="5" stroke="currentColor" stroke-width="1.5"/><path d="M6 3v3.5M6 8.5v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||
<span>对照左侧示例上传,审核更快通过</span>
|
||
</div>
|
||
</div>
|
||
</div><!-- /.fb-page-body -->
|
||
|
||
<div class="fb-page-footer">
|
||
<button class="fb-submit-btn" id="fbSubmitBtn" disabled onclick="submitFeedback()">提交反馈</button>
|
||
</div>
|
||
|
||
<!-- 截图示例 放大查看器 -->
|
||
<div class="fb-sample-modal" id="fbSampleModal" role="dialog" aria-modal="true" aria-label="截图示例">
|
||
<div class="fb-sample-modal-header">
|
||
<div class="fb-sample-modal-title">什么样的截图能通过审核</div>
|
||
<div class="fb-sample-modal-close" onclick="closeSampleModal()" role="button" aria-label="关闭">
|
||
<svg viewBox="0 0 14 14" fill="none"><path d="M2 2l10 10M12 2L2 12" stroke="#fff" stroke-width="2" stroke-linecap="round"/></svg>
|
||
</div>
|
||
</div>
|
||
<div class="fb-sample-modal-body">
|
||
<div class="fb-sample-fullimg">
|
||
<div class="fi-header">
|
||
<img src="logos/meituan.png" alt="">美团 · 订单详情
|
||
</div>
|
||
<div class="fi-block">
|
||
<div class="fi-shop">塔斯汀汉堡(朝阳大悦城店)</div>
|
||
<div class="fi-row"><span class="name">香辣鸡腿堡套餐 ×1</span><span class="price">¥17.80</span></div>
|
||
<div class="fi-row"><span class="name">配送费</span><span class="price">¥0.00</span></div>
|
||
<div class="fi-disc"><span>新人红包</span><span class="price">-¥5.00</span></div>
|
||
</div>
|
||
<div class="fi-total"><span class="lbl">实付</span><span class="val">¥12.80</span></div>
|
||
<div class="fb-sample-annot a1"><div class="num">1</div></div>
|
||
<div class="fb-sample-annot a2"><div class="num">2</div></div>
|
||
<div class="fb-sample-annot a3"><div class="num">3</div></div>
|
||
</div>
|
||
<div class="fb-sample-rules">
|
||
<div class="fb-sample-rules-title">截图需同时包含</div>
|
||
<div class="fb-sample-rules-item"><span class="n">①</span><span>商品名 / 店铺名清晰可见</span></div>
|
||
<div class="fb-sample-rules-item"><span class="n">②</span><span>领取的红包 / 优惠券金额</span></div>
|
||
<div class="fb-sample-rules-item"><span class="n">③</span><span><span class="em">使用红包后的实付价</span>(不是商品标价)</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 模拟 iOS 数字键盘(仅价格输入聚焦时显示) -->
|
||
<div class="fb-keyboard" id="fbKeyboard">
|
||
<div class="fb-key" data-k="1">1</div>
|
||
<div class="fb-key" data-k="2">2</div>
|
||
<div class="fb-key" data-k="3">3</div>
|
||
<div class="fb-key" data-k="4">4</div>
|
||
<div class="fb-key" data-k="5">5</div>
|
||
<div class="fb-key" data-k="6">6</div>
|
||
<div class="fb-key" data-k="7">7</div>
|
||
<div class="fb-key" data-k="8">8</div>
|
||
<div class="fb-key" data-k="9">9</div>
|
||
<div class="fb-key fn" data-k=".">.</div>
|
||
<div class="fb-key" data-k="0">0</div>
|
||
<div class="fb-key fn" data-k="DEL" aria-label="删除">
|
||
<svg width="26" height="20" viewBox="0 0 32 24" fill="none"><path d="M11 3 L29 3 A2 2 0 0 1 31 5 L31 19 A2 2 0 0 1 29 21 L11 21 L1 12 Z" stroke="#1A1A1A" stroke-width="1.5" fill="none"/><path d="M16 9 L24 15 M24 9 L16 15" stroke="#1A1A1A" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 菜品清单 bottom sheet(点 dish-fold › 触发;本页改为卡内展开,此 sheet 仅备用) -->
|
||
<div class="menu-mask" id="menuMask" onclick="closeMenuSheet()"></div>
|
||
<div class="menu-sheet" id="menuSheet" role="dialog" aria-modal="true">
|
||
<div class="menu-grip"></div>
|
||
<div class="menu-title-row">
|
||
<div class="menu-title" id="menuTitle"></div>
|
||
<div class="menu-count" id="menuCount"></div>
|
||
</div>
|
||
<div class="menu-date" id="menuDate"></div>
|
||
<div class="menu-list" id="menuList"></div>
|
||
<div class="menu-close" onclick="closeMenuSheet()">关闭</div>
|
||
</div>
|
||
|
||
<!-- 未登录态:骨架模糊 + 居中登录卡,不展示任何真实数据 -->
|
||
<div class="hist-logout-skeleton" aria-hidden="true">
|
||
<div class="hist-sk-item"><div class="hist-sk-line w60"></div><div class="hist-sk-line w30 sm"></div><div class="hist-sk-cells"><div class="hist-sk-cell"></div><div class="hist-sk-cell"></div><div class="hist-sk-cell"></div></div><div class="hist-sk-line w50 sm"></div></div>
|
||
<div class="hist-sk-item"><div class="hist-sk-line w50"></div><div class="hist-sk-line w30 sm"></div><div class="hist-sk-cells"><div class="hist-sk-cell"></div><div class="hist-sk-cell"></div><div class="hist-sk-cell"></div></div><div class="hist-sk-line w60 sm"></div></div>
|
||
<div class="hist-sk-item"><div class="hist-sk-line w60"></div><div class="hist-sk-line w30 sm"></div><div class="hist-sk-cells"><div class="hist-sk-cell"></div><div class="hist-sk-cell"></div><div class="hist-sk-cell"></div></div></div>
|
||
</div>
|
||
<div class="hist-logout-scrim" onclick="onEmptyLogin()"></div>
|
||
<div class="hist-empty-logout">
|
||
<div class="hist-login-card">
|
||
<div class="lk"><svg viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="10" width="16" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></div>
|
||
<div class="empty-title">登录后查看你的比价记录</div>
|
||
<button class="empty-cta" onclick="onEmptyLogin()">去登录</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- toast -->
|
||
<div class="toast" id="toast"></div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
/* ============================================================
|
||
* 数据层(照搬昨天手写版,已真机验证)
|
||
* FIXED_PLATFORMS / PLATFORM_ALIASES / resolvePlatformId /
|
||
* MOCK(浏览器预览)/ normalizeRecords / loadRecords
|
||
* ============================================================ */
|
||
|
||
// ===== 三个固定平台(顺序即网格顺序)。id 用客户端【规范 ID】,与原生 FIXED_PLATFORMS 一致 =====
|
||
// logo 用产物同级 logos/ 下的 png(原型路径 ../../assets/shared/logos/ → 拷到本地)
|
||
const FIXED_PLATFORMS = [
|
||
{ id: 'meituan-waimai', name: '美团', color: '#FFB300', logo: 'logos/meituan.png' },
|
||
{ id: 'taobao-shanguang', name: '淘宝', color: '#FF5000', logo: 'logos/taobao.png' },
|
||
{ id: 'jd-waimai', name: '京东', color: '#E1251B', logo: 'logos/jd.png' },
|
||
];
|
||
|
||
// ===== 平台别名归一化(照搬原生 PlatformIdMap.kt)=====
|
||
// 后端 platform_id 是短名(meituan/taobao_flash/jd_waimai)、platform_name 是中文(美团/淘宝闪购/京东外卖),
|
||
// 都要先归一化成上面的规范 id,才能对上三个格子。找不到返回 null。
|
||
const PLATFORM_ALIASES = {};
|
||
[
|
||
['meituan-waimai', ['meituan','meituan-waimai','meituan_waimai','美团','美团外卖']],
|
||
['taobao-shanguang', ['taobao_flash','taobao-shanguang','taobao_shanguang','taobao','淘宝闪购','淘宝']],
|
||
['jd-waimai', ['jd_waimai','jd_waimai_standalone','jd-waimai','jingdong-waimai','jingdong_waimai','jingdong','jd','京东外卖','京东','京东到家']],
|
||
].forEach(function (pair) {
|
||
pair[1].forEach(function (a) { PLATFORM_ALIASES[a.toLowerCase()] = pair[0]; });
|
||
});
|
||
function resolvePlatformId(idOrName) {
|
||
return PLATFORM_ALIASES[String(idOrName || '').toLowerCase()] || null;
|
||
}
|
||
|
||
// ===== 假数据(仅电脑浏览器预览用;装进 App 后走真接口)=====
|
||
// platformId 用后端真实会返回的【短名】,正好顺带验证上面的归一化生效。
|
||
// 覆盖:已下单/未下单、菜品折叠(≥4)/不折叠(≤3)、最低价在不同平台、某平台没比到价
|
||
const MOCK = [
|
||
{
|
||
id: 101, bestPriceCents: 3500, bestPlatformId: 'taobao-shanguang',
|
||
storeName: '麦当劳&麦咖啡(北京杨林DT店)', ordered: true,
|
||
items: [{name:'巨无霸'},{name:'薯条(大)'},{name:'可乐'},{name:'麦辣鸡翅'},{name:'圣代'}],
|
||
createdAt: '2026-06-12 14:30',
|
||
comparisonResults: [
|
||
{ platformId:'meituan', price:38.5, rank:2 },
|
||
{ platformId:'taobao_flash', price:35.0, rank:1 },
|
||
{ platformId:'jd_waimai', price:null },
|
||
],
|
||
},
|
||
{
|
||
id: 102, bestPriceCents: 1910, bestPlatformId: 'taobao-shanguang',
|
||
storeName: '麦稻中式健康菜(首都机场店)', ordered: false,
|
||
items: [{name:'秘制口水鸡'},{name:'干锅土豆片'},{name:'白米饭'}],
|
||
createdAt: '2026-06-11 19:05',
|
||
comparisonResults: [
|
||
{ platformId:'meituan', price:22.5, rank:2 },
|
||
{ platformId:'taobao_flash', price:19.1, rank:1 },
|
||
{ platformId:'jd_waimai', price:null },
|
||
],
|
||
},
|
||
{
|
||
id: 103, bestPriceCents: 1590, bestPlatformId: 'taobao-shanguang',
|
||
storeName: '瑞幸咖啡(望京SOHO店)', ordered: true,
|
||
items: [{name:'生椰拿铁'}],
|
||
createdAt: '2026-06-10 09:12',
|
||
comparisonResults: [
|
||
{ platformId:'meituan', price:16.9, rank:2 },
|
||
{ platformId:'taobao_flash', price:15.9, rank:1 },
|
||
{ platformId:'jd_waimai', price:18.0, rank:3 },
|
||
],
|
||
},
|
||
];
|
||
|
||
// ===== 数据加载:App 里走真接口,浏览器预览走假数据 =====
|
||
// 后端 JSON 是 snake_case(store_name / comparison_results / platform_id …),统一转成本页渲染用的字段名。
|
||
function normalizeRecords(items) {
|
||
return (items || []).map(function (r) {
|
||
return {
|
||
// id / bestPriceCents / bestPlatformId:上报流程要用——提交带记录 id、校验「比原最低价更低」要原价(分)
|
||
id: r.id,
|
||
bestPriceCents: r.best_price_cents,
|
||
bestPlatformId: r.best_platform_id,
|
||
storeName: r.store_name,
|
||
ordered: !!r.ordered,
|
||
items: r.items || [],
|
||
createdAt: String(r.created_at || '').replace('T', ' ').slice(0, 16),
|
||
comparisonResults: (r.comparison_results || []).map(function (c) {
|
||
return { platformId: c.platform_id, platformName: c.platform_name, price: c.price, rank: c.rank };
|
||
}),
|
||
};
|
||
});
|
||
}
|
||
|
||
// 同源部署:base 恒为空串,fetch 走相对路径 /api/v1/...(不依赖原生给域名)。
|
||
async function loadRecords() {
|
||
const hasBridge = window.SGBridge && typeof SGBridge.getToken === 'function';
|
||
if (hasBridge) {
|
||
const token = SGBridge.getToken();
|
||
if (!token) return null; // 未登录 → "登录后查看"
|
||
const base = '';
|
||
try {
|
||
const res = await fetch(base + '/api/v1/compare/records?limit=50',
|
||
{ headers: { 'Authorization': 'Bearer ' + token } });
|
||
if (res.status === 401) { SGBridge.requestLogin && SGBridge.requestLogin(); return null; }
|
||
const data = await res.json();
|
||
return normalizeRecords(data.items || []);
|
||
} catch (e) { /* 网络失败,落回预览数据 */ }
|
||
}
|
||
return MOCK; // 浏览器预览(无 Bridge)
|
||
}
|
||
|
||
/* ============================================================
|
||
* 列表渲染(用原型的 class,把 6 张写死卡片改成动态生成)
|
||
* ============================================================ */
|
||
|
||
// 全局记录与状态。RECORDS===null 表示未登录态(由 loadRecords 返回 null 触发)
|
||
let RECORDS = [];
|
||
const expandedSet = new Set(); // 记录哪些卡片菜品已展开(按 record 在 RECORDS 中的下标)
|
||
|
||
function esc(s){ return String(s == null ? '' : s).replace(/[&<>"]/g, function(c){ return {'&':'&','<':'<','>':'>','"':'"'}[c]; }); }
|
||
|
||
// 搜索高亮:用原型的 .search-hl 类(橙红加粗),命中关键词包一层
|
||
function highlight(text, q){
|
||
var safe = esc(text);
|
||
if (!q) return safe;
|
||
var qe = esc(q).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||
return safe.replace(new RegExp('(' + qe + ')', 'gi'), '<span class="search-hl">$1</span>');
|
||
}
|
||
|
||
// 三平台格:固定顺序 美团/淘宝/京东,按数据三态生成
|
||
// ① 有价正常;② rank==1 → .lowest + .hist-pl-corner「最低」;③ 无记录 → .no-store +「店家未入驻」
|
||
function renderPlatformCells(rec, q){
|
||
return FIXED_PLATFORMS.map(function (fp) {
|
||
var r = (rec.comparisonResults || []).find(function (x) {
|
||
return resolvePlatformId(x.platformId || x.platformName) === fp.id;
|
||
});
|
||
var hasPrice = r && r.price != null;
|
||
var head = '<div class="hist-pl-head">' +
|
||
'<img class="hist-pl-logo" src="' + fp.logo + '" alt=""><span class="hist-pl-name">' + fp.name + '</span></div>';
|
||
if (!hasPrice) {
|
||
// 态③:该平台无记录 → 店家未入驻
|
||
return '<div class="hist-pl-cell no-store">' + head +
|
||
'<div class="hist-pl-status">店家未入驻</div></div>';
|
||
}
|
||
var lowest = r.rank === 1;
|
||
var priceTxt = '¥' + Number(r.price).toFixed(2);
|
||
// 态②最低:.lowest + 角标;态①普通:仅价格
|
||
return '<div class="hist-pl-cell' + (lowest ? ' lowest' : '') + '">' + head +
|
||
(lowest ? '<span class="hist-pl-corner">最低</span>' : '') +
|
||
'<div class="hist-pl-price">' + priceTxt + '</div></div>';
|
||
}).join('');
|
||
}
|
||
|
||
// 菜品块:≤3 件用 .hist-dish-full(直接列名);≥4 件用 .hist-dish-fold(单行省略 + 等N件 + 箭头,点击卡内展开)
|
||
function renderDishBlock(rec, idx, q){
|
||
var items = rec.items || [];
|
||
var names = items.map(function(i){ return i.name; }).filter(Boolean);
|
||
if (names.length <= 3) {
|
||
return '<div class="hist-dish-full">' + highlight(names.join('、'), q) + '</div>';
|
||
}
|
||
var isOpen = expandedSet.has(idx);
|
||
return '<div class="hist-dish-fold" onclick="toggleDishExpand(event)" role="button" aria-expanded="' + (isOpen ? 'true' : 'false') + '">' +
|
||
'<span class="hist-dish-fold-names">' + highlight(names.join('、'), q) + '</span>' +
|
||
'<span class="hist-dish-fold-count">等 ' + names.length + ' 件</span>' +
|
||
'<span class="hist-dish-fold-arrow" aria-hidden="true"><svg viewBox="0 0 12 12" fill="none"><path d="M3.2 4.7 6 7.3 8.8 4.7" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>' +
|
||
'</div>';
|
||
}
|
||
|
||
// 单张卡片:结构/class 对齐原型,且把 dish 放在 platforms 之前、meta 放进 actions(= 原型 time-compact 处理后的最终布局)
|
||
// data-idx 供搜索 / 折叠 / 单选回查 record
|
||
function renderHistItem(rec, idx, q){
|
||
var head = '<div class="hist-item-head">' +
|
||
'<div class="hist-store">' + highlight(rec.storeName || '比价', q) + '</div>' +
|
||
(rec.ordered ? '<span class="hist-ordered-chip">已下单</span>' : '') +
|
||
'</div>';
|
||
var meta = '<div class="hist-item-meta"><div class="hist-date">' + esc(rec.createdAt || '') + '</div></div>';
|
||
var dish = renderDishBlock(rec, idx, q);
|
||
var platforms = '<div class="hist-platforms">' + renderPlatformCells(rec, q) + '</div>';
|
||
// actions 区:time-compact 下 meta 在最左、再比一次按钮在最右(justify-content:space-between)
|
||
var actions = '<div class="hist-card-actions">' + meta +
|
||
'<button class="hist-recompare-btn" type="button" onclick="onRecompare(event)">再比一次</button></div>';
|
||
var item = document.createElement('div');
|
||
item.className = 'hist-item';
|
||
item.setAttribute('data-idx', String(idx));
|
||
item.innerHTML = head + dish + platforms + actions;
|
||
return item;
|
||
}
|
||
|
||
// 渲染整个列表到 #histList。currentFilter='all'|'ordered',q=搜索关键词(搜索态由 onSearchInput 传)
|
||
var currentFilter = 'all';
|
||
|
||
function renderList(q){
|
||
var listEl = document.getElementById('histList');
|
||
if (!listEl) return;
|
||
listEl.innerHTML = '';
|
||
|
||
// 未登录态:由 setLoggedIn 控制骨架/登录卡显隐,这里不渲染真实数据
|
||
if (RECORDS === null) return;
|
||
|
||
// 单选模式下重渲染要保留 radio + 点击绑定,故渲染后若在 selecting 态需补挂
|
||
RECORDS.forEach(function (rec, idx) {
|
||
var node = renderHistItem(rec, idx, q || '');
|
||
// tab 过滤:已下单 tab 只显示 ordered
|
||
if (currentFilter === 'ordered' && !rec.ordered) node.classList.add('filter-hide-ordered');
|
||
listEl.appendChild(node);
|
||
});
|
||
|
||
// 应用筛选(用原型一致的 display 方式)
|
||
applyHistFilter(currentFilter);
|
||
|
||
// 渲染后若一张可见卡片都没有(无记录 / 该分类无结果 / 搜索无结果)→ 出空态文案,别留纯空白
|
||
var visibleCount = 0;
|
||
listEl.querySelectorAll('.hist-item').forEach(function (it) {
|
||
if (it.style.display !== 'none' && !it.classList.contains('filter-hide-ordered')) visibleCount++;
|
||
});
|
||
if (visibleCount === 0) {
|
||
var emptyMsg = (RECORDS.length === 0) ? '还没有比价记录'
|
||
: (q ? '没有找到匹配的记录' : '该分类下暂无记录');
|
||
listEl.insertAdjacentHTML('beforeend',
|
||
'<div class="hist-empty-tip" style="text-align:center;color:#999;font-size:14px;padding:64px 16px;line-height:1.6;">' + emptyMsg + '</div>');
|
||
}
|
||
|
||
// 若处于单选态,补挂 radio 与点击
|
||
if (document.querySelector('.device').classList.contains('selecting')) {
|
||
decorateItemsForSelect();
|
||
}
|
||
}
|
||
|
||
/* ============================================================
|
||
* 以下为原型脚本(导航占位 / 筛选 / 搜索 / 折叠 / 单选 / 反馈 sheet / 键盘),
|
||
* 改动点:①导航 go()/onRecompare() 改为占位;②筛选 applyHistFilter 适配动态卡片;
|
||
* ③搜索 onSearchInput 驱动动态列表并走 renderList 高亮;④折叠 toggleDishExpand 走 expandedSet 重渲染。
|
||
* ============================================================ */
|
||
|
||
// reportCta 变体(保留原型逻辑,默认 reward)
|
||
(function() {
|
||
var variant = 'reward';
|
||
try {
|
||
var q = new URLSearchParams(location.search).get('reportCta');
|
||
if (q === 'outline' || q === 'solid' || q === 'reward') variant = q;
|
||
} catch(e) {}
|
||
var device = document.querySelector('.device');
|
||
if (device) device.classList.add('cta-' + variant);
|
||
})();
|
||
|
||
// ?from=profile:隐藏底栏 + 显示左上返回(保留原型逻辑;底栏本就隐藏,这里仅控制返回箭头)
|
||
var historyFromProfile = false;
|
||
(function() {
|
||
try { historyFromProfile = new URLSearchParams(location.search).get('from') === 'profile'; } catch(e) {}
|
||
var device = document.querySelector('.device');
|
||
if (historyFromProfile && device) device.classList.add('profile-entry');
|
||
})();
|
||
|
||
// 返回「我的」:?from=profile 进来时顶栏返回箭头调原生 closePage 回上一页(WebViewScreen onBack→popBackStack);浏览器预览仅 log
|
||
function goBackToProfile() {
|
||
if (window.SGBridge && typeof SGBridge.closePage === 'function') { SGBridge.closePage(); }
|
||
else { console.log('[nav stub] goBackToProfile(浏览器预览)'); }
|
||
}
|
||
|
||
// 导航占位:原型内部跳转(home/profile/reports/welfare/comparing…)在 App 里走不通,统一打 log
|
||
function navigateStub(id) {
|
||
console.log('[nav stub] go("' + id + '")(App 内由原生处理,H5 不跳转)');
|
||
}
|
||
window.go = navigateStub;
|
||
window.goBack = function() { console.log('[nav stub] goBack'); };
|
||
|
||
// 「上报记录」入口:App 内经原生桥跳上报记录页(REPORT_RECORDS);浏览器预览(无原生 navigate)走 mock。
|
||
function goReports() {
|
||
if (window.SGBridge && SGBridge.navigate) { SGBridge.navigate('reportRecords'); }
|
||
else { go('reports'); }
|
||
}
|
||
window.goReports = goReports;
|
||
|
||
// ========== 筛选 tab ==========
|
||
function onFilterClick(tab) {
|
||
if (document.querySelector('.device').classList.contains('logged-out')) { onEmptyLogin(); return; }
|
||
var allTabs = document.querySelectorAll('.hist-filter-tab');
|
||
var prev = document.querySelector('.hist-filter-tab.active');
|
||
if (prev === tab) return;
|
||
var prevIdx = Array.prototype.indexOf.call(allTabs, prev);
|
||
var newIdx = Array.prototype.indexOf.call(allTabs, tab);
|
||
var direction = newIdx > prevIdx ? 1 : -1; // 1 = 切到右 tab (内容从右进), -1 = 切到左 tab
|
||
allTabs.forEach(function(t) { t.classList.remove('active'); });
|
||
tab.classList.add('active');
|
||
currentFilter = tab.dataset.filter;
|
||
slideHistFilter(tab.dataset.filter, direction);
|
||
}
|
||
|
||
// 应用 filter:动态卡片上按 ordered chip 判定显隐(与原型同款 display 切换)
|
||
function applyHistFilter(filter) {
|
||
document.querySelectorAll('.hist-item').forEach(function(item) {
|
||
var hasOrdered = !!item.querySelector('.hist-ordered-chip');
|
||
if (filter === 'ordered') item.style.display = hasOrdered ? '' : 'none';
|
||
else item.style.display = ''; // 'all'
|
||
});
|
||
}
|
||
|
||
// 切换 filter 时的横向 slide + fade 动画(替代瞬间 display 切换)
|
||
function slideHistFilter(filter, direction) {
|
||
var scroll = document.querySelector('.hist-scroll');
|
||
var dist = 36;
|
||
// 旧内容朝反方向滑出 + 淡出
|
||
scroll.style.transition = 'transform .18s ease-out, opacity .18s ease-out';
|
||
scroll.style.transform = 'translateX(' + (-direction * dist) + 'px)';
|
||
scroll.style.opacity = '0';
|
||
setTimeout(function() {
|
||
applyHistFilter(filter);
|
||
scroll.scrollTop = 0;
|
||
// 把容器瞬移到反方向,准备从那里滑回 0
|
||
scroll.style.transition = 'none';
|
||
scroll.style.transform = 'translateX(' + (direction * dist) + 'px)';
|
||
void scroll.offsetHeight; // 强制 reflow
|
||
// 新内容滑入 + 淡入
|
||
scroll.style.transition = 'transform .24s cubic-bezier(.2,.8,.2,1), opacity .22s ease-in';
|
||
scroll.style.transform = 'translateX(0)';
|
||
scroll.style.opacity = '1';
|
||
}, 180);
|
||
}
|
||
|
||
// 左右滑动切换 tab —— 触摸 + 鼠标拖动都支持
|
||
(function() {
|
||
var scroll = document.querySelector('.hist-scroll');
|
||
if (!scroll) return;
|
||
var startX = null, startY = null;
|
||
function onStart(x, y) { startX = x; startY = y; }
|
||
function onEnd(x, y) {
|
||
if (startX === null) return;
|
||
var dx = x - startX, dy = y - startY;
|
||
startX = null; startY = null;
|
||
if (Math.abs(dx) < 60 || Math.abs(dy) > 40) return; // 阈值 + 垂直滑动忽略
|
||
var tabs = Array.prototype.slice.call(document.querySelectorAll('.hist-filter-tab'));
|
||
var active = document.querySelector('.hist-filter-tab.active');
|
||
var idx = tabs.indexOf(active);
|
||
if (dx < 0 && idx < tabs.length - 1) onFilterClick(tabs[idx + 1]); // 左滑 → 下一个 tab
|
||
else if (dx > 0 && idx > 0) onFilterClick(tabs[idx - 1]); // 右滑 → 上一个 tab
|
||
}
|
||
scroll.addEventListener('touchstart', function(e) { if (e.touches.length === 1) onStart(e.touches[0].clientX, e.touches[0].clientY); }, { passive: true });
|
||
scroll.addEventListener('touchend', function(e) { onEnd(e.changedTouches[0].clientX, e.changedTouches[0].clientY); });
|
||
// 鼠标拖动模拟:mousedown 在 scroll 起,mouseup 绑到 window 上(防止鼠标拖出 device 外丢失事件)
|
||
scroll.addEventListener('mousedown', function(e) {
|
||
onStart(e.clientX, e.clientY);
|
||
var up = function(ev) {
|
||
onEnd(ev.clientX, ev.clientY);
|
||
window.removeEventListener('mouseup', up);
|
||
};
|
||
window.addEventListener('mouseup', up);
|
||
});
|
||
})();
|
||
|
||
// ========== 搜索 ==========
|
||
var SEARCH_HISTORY_KEY = 'proto_history_search_recent';
|
||
|
||
function enterSearchMode() {
|
||
var device = document.querySelector('.device');
|
||
if (device.classList.contains('logged-out')) { onEmptyLogin(); return; }
|
||
device.classList.add('searching');
|
||
device.classList.remove('has-input', 'no-results');
|
||
renderSearchHistory();
|
||
var input = document.getElementById('histSearchInput');
|
||
input.value = '';
|
||
setTimeout(function() { input.focus(); }, 80);
|
||
}
|
||
|
||
function exitSearchMode() {
|
||
var device = document.querySelector('.device');
|
||
device.classList.remove('searching', 'has-input', 'no-results');
|
||
document.getElementById('histSearchInput').value = '';
|
||
// 退出搜索:清掉高亮,按当前 filter 重渲染列表
|
||
renderList('');
|
||
document.getElementById('histSearchInput').blur();
|
||
}
|
||
|
||
function clearSearch() {
|
||
var input = document.getElementById('histSearchInput');
|
||
input.value = '';
|
||
onSearchInput('');
|
||
input.focus();
|
||
}
|
||
|
||
// 搜索输入:驱动动态列表(搜店名 + 菜名),命中高亮、无结果切空态
|
||
function onSearchInput(value) {
|
||
var device = document.querySelector('.device');
|
||
var q = (value || '').trim();
|
||
var qLower = q.toLowerCase();
|
||
if (qLower.length === 0) {
|
||
device.classList.remove('has-input', 'no-results');
|
||
renderList(''); // 清空高亮、恢复全列表
|
||
return;
|
||
}
|
||
device.classList.add('has-input');
|
||
// 重渲染(带高亮),再按命中加 search-hide
|
||
renderList(q);
|
||
var hitCount = 0;
|
||
document.querySelectorAll('.hist-item').forEach(function(item) {
|
||
var store = (item.querySelector('.hist-store') || {}).textContent || '';
|
||
var dishFull = (item.querySelector('.hist-dish-full') || {}).textContent || '';
|
||
var dishFoldNames = (item.querySelector('.hist-dish-fold-names') || {}).textContent || '';
|
||
var haystack = (store + ' ' + dishFull + ' ' + dishFoldNames).toLowerCase();
|
||
if (haystack.indexOf(qLower) >= 0) {
|
||
item.classList.remove('search-hide');
|
||
hitCount++;
|
||
} else {
|
||
item.classList.add('search-hide');
|
||
}
|
||
});
|
||
device.classList.toggle('no-results', hitCount === 0);
|
||
}
|
||
|
||
function commitSearchHistory(term) {
|
||
var t = (term || '').trim();
|
||
if (!t) return;
|
||
var list = readSearchHistory();
|
||
// 去重 + 置顶
|
||
list = [t].concat(list.filter(function(x) { return x !== t; })).slice(0, 8);
|
||
try { localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(list)); } catch(e) {}
|
||
}
|
||
|
||
function readSearchHistory() {
|
||
try {
|
||
var raw = localStorage.getItem(SEARCH_HISTORY_KEY);
|
||
return raw ? JSON.parse(raw) : [];
|
||
} catch(e) { return []; }
|
||
}
|
||
|
||
function renderSearchHistory() {
|
||
var list = readSearchHistory();
|
||
var chipsEl = document.getElementById('histSearchChips');
|
||
var emptyEl = document.getElementById('histSearchEmpty');
|
||
if (list.length === 0) {
|
||
chipsEl.innerHTML = '';
|
||
emptyEl.style.display = 'block';
|
||
return;
|
||
}
|
||
emptyEl.style.display = 'none';
|
||
chipsEl.innerHTML = list.map(function(term) {
|
||
var safe = term.replace(/</g, '<');
|
||
return '<div class="hist-search-chip" onclick="applySearchTerm(\'' + safe.replace(/'/g, "'") + '\')">' + safe + '</div>';
|
||
}).join('');
|
||
}
|
||
|
||
function applySearchTerm(term) {
|
||
var input = document.getElementById('histSearchInput');
|
||
input.value = term;
|
||
onSearchInput(term);
|
||
commitSearchHistory(term);
|
||
}
|
||
|
||
function clearSearchHistory() {
|
||
try { localStorage.removeItem(SEARCH_HISTORY_KEY); } catch(e) {}
|
||
renderSearchHistory();
|
||
}
|
||
|
||
// 输入框失焦或回车时,把当前关键词写入历史
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var input = document.getElementById('histSearchInput');
|
||
if (!input) return;
|
||
input.addEventListener('keydown', function(e) {
|
||
if (e.key === 'Enter') { commitSearchHistory(input.value); renderSearchHistory(); input.blur(); }
|
||
});
|
||
input.addEventListener('blur', function() {
|
||
if (input.value.trim()) commitSearchHistory(input.value);
|
||
});
|
||
});
|
||
|
||
// 点击未安装 cell → 引导下载该平台 APP(本页三态无 not-installed,保留函数兜底)
|
||
// 单选模式下不响应,让事件冒泡到 hist-item 选中
|
||
function onDownloadPlatform(platformId) {
|
||
var device = document.querySelector('.device');
|
||
if (device && device.classList.contains('selecting')) return;
|
||
var name = { 'meituan-waimai': '美团', 'taobao-shanguang': '淘宝', 'jd-waimai': '京东' }[platformId] || platformId;
|
||
showToast('去下载' + name);
|
||
}
|
||
|
||
// 再比一次:App 里走原生重新比价占位
|
||
function onRecompare(event) {
|
||
if (event) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
}
|
||
var device = document.querySelector('.device');
|
||
if (device && device.classList.contains('selecting')) return;
|
||
console.log('[stub] onRecompare(App 内由原生重新发起比价)');
|
||
showToast('正在再比一次');
|
||
}
|
||
|
||
// ========== 登录态 ==========
|
||
// 本页登录态以「有无 token」为准(loadRecords 返回 null = 未登录)。
|
||
// 同时保留 logout-toggle demo 钩子(仅浏览器预览用,App 内无此按钮)。
|
||
function setLoggedIn(loggedIn) {
|
||
var device = document.querySelector('.device');
|
||
if (!device) return;
|
||
if (loggedIn) device.classList.remove('logged-out');
|
||
else {
|
||
device.classList.add('logged-out');
|
||
device.classList.remove('selecting', 'searching', 'has-input', 'no-results');
|
||
}
|
||
}
|
||
function onEmptyLogin() {
|
||
// App 里点「去登录」交给原生(或 onUnauthorized 已触发跳登录)
|
||
console.log('[stub] onEmptyLogin(App 内由原生引导登录)');
|
||
if (window.SGBridge && typeof SGBridge.requestLogin === 'function') {
|
||
try { SGBridge.requestLogin(); } catch(e) {}
|
||
}
|
||
}
|
||
|
||
var toastTimer = null;
|
||
function showToast(msg) {
|
||
var t = document.getElementById('toast');
|
||
if (!t) return;
|
||
t.textContent = msg;
|
||
t.classList.add('show');
|
||
if (toastTimer) clearTimeout(toastTimer);
|
||
toastTimer = setTimeout(function() { t.classList.remove('show'); }, 1500);
|
||
}
|
||
|
||
// ========== 菜品清单 bottom sheet(本页改卡内展开,sheet 仅保留 DOM;openMenuSheet 兜底)==========
|
||
function openMenuSheet() { /* 本页用卡内展开,不再用底部 sheet */ }
|
||
function closeMenuSheet() {
|
||
document.getElementById('menuMask').classList.remove('show');
|
||
document.getElementById('menuSheet').classList.remove('show');
|
||
}
|
||
|
||
function setDishExpanded(item, expanded) {
|
||
item.classList.toggle('dish-expanded', expanded);
|
||
var trigger = item.querySelector('.hist-dish-fold');
|
||
if (trigger) trigger.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
||
}
|
||
|
||
// 菜品折叠:点 dish-fold 在卡内展开 / 收起。走 expandedSet 记状态(重渲染也保留),手风琴式互斥。
|
||
function toggleDishExpand(event) {
|
||
if (event) event.stopPropagation();
|
||
var trigger = event && event.currentTarget;
|
||
var item = trigger && trigger.closest ? trigger.closest('.hist-item') : null;
|
||
if (!item) return;
|
||
var device = document.querySelector('.device');
|
||
if (device && device.classList.contains('selecting')) return;
|
||
var idx = parseInt(item.getAttribute('data-idx'), 10);
|
||
var shouldExpand = !item.classList.contains('dish-expanded');
|
||
// 手风琴:先收起其它已展开的
|
||
expandedSet.clear();
|
||
document.querySelectorAll('.hist-item.dish-expanded').forEach(function(openItem) {
|
||
if (openItem !== item) setDishExpanded(openItem, false);
|
||
});
|
||
setDishExpanded(item, shouldExpand);
|
||
if (shouldExpand && !isNaN(idx)) expandedSet.add(idx);
|
||
}
|
||
|
||
function setFeedbackTargetDish(dishFull, dishFold) {
|
||
var dishEl = document.getElementById('fbTargetDish');
|
||
if (!dishEl) return;
|
||
dishEl.className = 'fb-target-dish';
|
||
dishEl.innerHTML = '';
|
||
dishEl.onclick = null;
|
||
dishEl.removeAttribute('role');
|
||
dishEl.removeAttribute('aria-expanded');
|
||
|
||
if (dishFull) {
|
||
dishEl.textContent = dishFull.textContent.trim();
|
||
return;
|
||
}
|
||
|
||
if (!dishFold) return;
|
||
var namesText = ((dishFold.querySelector('.hist-dish-fold-names') || {}).textContent || '').trim();
|
||
var countText = ((dishFold.querySelector('.hist-dish-fold-count') || {}).textContent || '').trim();
|
||
dishEl.classList.add('is-fold');
|
||
dishEl.setAttribute('role', 'button');
|
||
dishEl.setAttribute('aria-expanded', 'false');
|
||
dishEl.onclick = toggleFeedbackTargetDish;
|
||
|
||
var names = document.createElement('span');
|
||
names.className = 'fb-target-dish-names';
|
||
names.textContent = namesText;
|
||
dishEl.appendChild(names);
|
||
|
||
if (countText) {
|
||
var count = document.createElement('span');
|
||
count.className = 'fb-target-dish-count';
|
||
count.textContent = countText;
|
||
dishEl.appendChild(count);
|
||
}
|
||
|
||
var arrow = document.createElement('span');
|
||
arrow.className = 'fb-target-dish-arrow';
|
||
arrow.setAttribute('aria-hidden', 'true');
|
||
arrow.innerHTML = '<svg viewBox="0 0 12 12" fill="none"><path d="M3.2 4.7 6 7.3 8.8 4.7" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
||
dishEl.appendChild(arrow);
|
||
}
|
||
|
||
function toggleFeedbackTargetDish(event) {
|
||
if (event) event.stopPropagation();
|
||
var dishEl = document.getElementById('fbTargetDish');
|
||
if (!dishEl || !dishEl.classList.contains('is-fold')) return;
|
||
var expanded = !dishEl.classList.contains('expanded');
|
||
dishEl.classList.toggle('expanded', expanded);
|
||
dishEl.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
||
}
|
||
|
||
// ========== 反馈低价 · 单选模式 ==========
|
||
var selectMode = false;
|
||
var selectedItem = null; // 选中卡片的 DOM(驱动 radio 高亮 / 下一步可用)
|
||
var selectedRecord = null; // 选中卡片对应的 record 数据(从 RECORDS[data-idx] 回查;提交/校验用它,不再从 DOM 抠)
|
||
var selectedPlatform = null;
|
||
// 是否由「我的 · 上报更低价」深链进入(?report=1):决定取消时返回「我的」而非停留在记录页
|
||
var cameFromProfile = new URLSearchParams(location.search).get('report') === '1';
|
||
|
||
// 给当前所有 .hist-item 挂上 radio + 选中点击(渲染后调用;重渲染也要补挂)
|
||
function decorateItemsForSelect() {
|
||
document.querySelectorAll('.hist-item').forEach(function(item) {
|
||
if (!item.querySelector('.hist-item-radio')) {
|
||
var radio = document.createElement('div');
|
||
radio.className = 'hist-item-radio';
|
||
item.appendChild(radio);
|
||
}
|
||
item.classList.remove('selected');
|
||
item.onclick = function() { onItemSelect(item); };
|
||
});
|
||
}
|
||
|
||
function enterSelectMode() {
|
||
if (document.querySelector('.device').classList.contains('logged-out')) { onEmptyLogin(); return; }
|
||
selectMode = true;
|
||
selectedItem = null;
|
||
document.querySelector('.device').classList.add('selecting');
|
||
decorateItemsForSelect();
|
||
refreshNextBtn();
|
||
}
|
||
|
||
// 取消上报:深链(从「我的」进入)则返回「我的」,否则仅退出选择态停留在记录页
|
||
function cancelSelect() {
|
||
if (cameFromProfile) { goBackToProfile(); return; }
|
||
exitSelectMode();
|
||
}
|
||
|
||
function exitSelectMode() {
|
||
selectMode = false;
|
||
selectedItem = null;
|
||
selectedRecord = null;
|
||
document.querySelector('.device').classList.remove('selecting');
|
||
document.querySelectorAll('.hist-item').forEach(function(item) {
|
||
item.classList.remove('selected');
|
||
item.onclick = null;
|
||
});
|
||
}
|
||
|
||
function onItemSelect(item) {
|
||
document.querySelectorAll('.hist-item').forEach(function(i) { i.classList.remove('selected'); });
|
||
item.classList.add('selected');
|
||
selectedItem = item;
|
||
refreshNextBtn();
|
||
}
|
||
|
||
function refreshNextBtn() {
|
||
var btn = document.getElementById('fbNextBtn');
|
||
if (btn) btn.disabled = !selectedItem;
|
||
}
|
||
|
||
// ========== 反馈 bottom-sheet ==========
|
||
var photoUploaded = false;
|
||
var selectedFiles = []; // 已选截图 File 列表(≤4),提交时压缩后逐张 append
|
||
|
||
function openFeedbackSheet() {
|
||
if (!selectedItem) return;
|
||
// 从卡片的 data-idx 回查 record(不再从 DOM textContent 抠,拿不到 id/bestPriceCents)
|
||
var idx = parseInt(selectedItem.getAttribute('data-idx'), 10);
|
||
selectedRecord = (RECORDS && idx >= 0) ? RECORDS[idx] : null;
|
||
if (!selectedRecord) return;
|
||
|
||
// 展示沿用原逻辑:仍从选中卡片的 DOM 取店名/日期/菜品/最低价行(纯展示,无须 id)
|
||
var store = (selectedItem.querySelector('.hist-store') || {}).textContent || '';
|
||
var date = (selectedItem.querySelector('.hist-date') || {}).textContent || '';
|
||
// 菜品:dish-full 普通展示;dish-fold 保留「等 N 件」并支持在当前卡片内展开
|
||
var dishFull = selectedItem.querySelector('.hist-dish-full');
|
||
var dishFold = selectedItem.querySelector('.hist-dish-fold');
|
||
var lowestRow = selectedItem.querySelector('.hist-pl-cell.lowest');
|
||
var price = lowestRow ? (lowestRow.querySelector('.hist-pl-price') || {}).textContent || '' : '';
|
||
var platform = lowestRow ? (lowestRow.querySelector('.hist-pl-name') || {}).textContent || '' : '';
|
||
var plLogoEl = lowestRow ? lowestRow.querySelector('.hist-pl-logo') : null;
|
||
var plLogoSrc = plLogoEl ? plLogoEl.src : '';
|
||
|
||
document.getElementById('fbTargetStore').textContent = store;
|
||
document.getElementById('fbTargetDate').textContent = date;
|
||
setFeedbackTargetDish(dishFull, dishFold);
|
||
document.getElementById('fbTargetPrice').textContent = price;
|
||
document.getElementById('fbTargetPlatform').textContent = platform;
|
||
var plLogoImg = document.getElementById('fbTargetPlLogo');
|
||
if (plLogoSrc) { plLogoImg.src = plLogoSrc; plLogoImg.style.display = ''; }
|
||
else { plLogoImg.style.display = 'none'; }
|
||
|
||
// 重置表单
|
||
var priceInput = document.getElementById('fbPriceInput');
|
||
priceInput.value = '';
|
||
photoUploaded = false;
|
||
selectedPlatform = null;
|
||
resetSelectedFiles();
|
||
document.querySelectorAll('.fb-platform-chip').forEach(function(c) { c.classList.remove('active'); });
|
||
refreshSubmitBtn();
|
||
|
||
// 显示
|
||
document.getElementById('fbSheet').classList.add('show');
|
||
|
||
// 进入即拉起模拟键盘 + 聚焦价格输入框
|
||
showKeyboard();
|
||
}
|
||
|
||
function closeFeedbackSheet() {
|
||
document.getElementById('fbSheet').classList.remove('show');
|
||
hideKeyboard();
|
||
}
|
||
|
||
// ===== 截图示例 modal =====
|
||
function openSampleModal() {
|
||
hideKeyboard();
|
||
document.getElementById('fbSampleModal').classList.add('show');
|
||
}
|
||
function closeSampleModal() {
|
||
document.getElementById('fbSampleModal').classList.remove('show');
|
||
}
|
||
|
||
// ===== 模拟键盘 =====
|
||
function showKeyboard() {
|
||
var device = document.querySelector('.device');
|
||
device.classList.add('kb-up');
|
||
document.getElementById('fbKeyboard').classList.add('show');
|
||
var priceInput = document.getElementById('fbPriceInput');
|
||
// 真机上 focus 会调起系统键盘,桌面 demo 上只为了显示焦点描边
|
||
try { priceInput.focus({ preventScroll: true }); } catch(e) { priceInput.focus(); }
|
||
}
|
||
|
||
function hideKeyboard() {
|
||
var device = document.querySelector('.device');
|
||
device.classList.remove('kb-up');
|
||
document.getElementById('fbKeyboard').classList.remove('show');
|
||
if (document.activeElement && document.activeElement.blur) document.activeElement.blur();
|
||
}
|
||
|
||
// 模拟键盘按键 → 写入价格输入框
|
||
document.querySelectorAll('.fb-key').forEach(function(key) {
|
||
key.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
var input = document.getElementById('fbPriceInput');
|
||
var k = key.dataset.k;
|
||
var v = input.value || '';
|
||
if (k === 'DEL') {
|
||
input.value = v.slice(0, -1);
|
||
} else if (k === '.') {
|
||
if (v.indexOf('.') === -1) input.value = v + '.';
|
||
} else {
|
||
// 数字键
|
||
var next = (v === '0') ? k : v + k; // 替代前导 0
|
||
var dot = next.indexOf('.');
|
||
if (!(dot >= 0 && next.length - dot - 1 > 2)) input.value = next; // 小数点后最多 2 位
|
||
}
|
||
refreshSubmitBtn();
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('.fb-platform-chip').forEach(function(chip) {
|
||
chip.addEventListener('click', function() {
|
||
document.querySelectorAll('.fb-platform-chip').forEach(function(c) { c.classList.remove('active'); });
|
||
chip.classList.add('active');
|
||
selectedPlatform = chip.dataset.pf;
|
||
refreshSubmitBtn();
|
||
});
|
||
});
|
||
|
||
function refreshSubmitBtn() {
|
||
var price = parseFloat(document.getElementById('fbPriceInput').value);
|
||
// 价格 + 平台 + 截图 三必填
|
||
var ok = price > 0 && selectedPlatform && photoUploaded;
|
||
document.getElementById('fbSubmitBtn').disabled = !ok;
|
||
}
|
||
|
||
// 点上传槽 → 触发隐藏 file input(真机由原生 onShowFileChooser 接管,弹系统相册多选)
|
||
function onPickPhoto() {
|
||
var input = document.getElementById('fbFileInput');
|
||
if (input) input.click();
|
||
}
|
||
|
||
// 上传槽缩略图里已有的 objectURL,重置/重选时要 revoke 防内存泄漏
|
||
var _thumbUrls = [];
|
||
function _revokeThumbUrls() {
|
||
_thumbUrls.forEach(function (u) { try { URL.revokeObjectURL(u); } catch (e) {} });
|
||
_thumbUrls = [];
|
||
}
|
||
|
||
// 清空已选图(打开 sheet / 提交成功后调用)
|
||
function resetSelectedFiles() {
|
||
selectedFiles = [];
|
||
photoUploaded = false;
|
||
_revokeThumbUrls();
|
||
var input = document.getElementById('fbFileInput');
|
||
if (input) input.value = ''; // 清掉 input 的 files,下次选同图也能触发 change
|
||
renderUploadSlot();
|
||
}
|
||
|
||
// file input 选完回调:取 files(≤4,超出截断并提示)→ 存全局 → 渲染缩略图
|
||
function onPhotoFilesPicked(input) {
|
||
var files = Array.prototype.slice.call(input.files || []);
|
||
if (files.length === 0) return; // 用户取消选择,保持原状
|
||
var truncated = false;
|
||
if (files.length > 4) { files = files.slice(0, 4); truncated = true; }
|
||
selectedFiles = files;
|
||
photoUploaded = selectedFiles.length > 0;
|
||
renderUploadSlot();
|
||
refreshSubmitBtn();
|
||
if (truncated) showToast('最多上传 4 张');
|
||
}
|
||
|
||
// 渲染上传槽:有图 → 显示第一张缩略图 + 已选张数;无图 → 复原「点击添加」
|
||
function renderUploadSlot() {
|
||
var slot = document.getElementById('fbUploadSlot');
|
||
if (!slot) return;
|
||
_revokeThumbUrls();
|
||
if (selectedFiles.length > 0) {
|
||
var url = URL.createObjectURL(selectedFiles[0]);
|
||
_thumbUrls.push(url);
|
||
slot.classList.add('uploaded');
|
||
// 缩略图铺满槽 + 右下角张数角标(行内样式即可,沿用 80x80 槽尺寸)
|
||
slot.innerHTML =
|
||
'<img src="' + url + '" alt="" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:10px;">' +
|
||
'<span style="position:absolute;right:3px;bottom:3px;padding:1px 6px;border-radius:8px;background:rgba(0,0,0,.6);color:#fff;font-size:11px;font-weight:600;line-height:1.5;">' + selectedFiles.length + '/4</span>';
|
||
} else {
|
||
slot.classList.remove('uploaded');
|
||
slot.innerHTML = '<span class="fb-upload-plus" aria-hidden="true"></span><span class="fb-upload-text">点击添加</span>';
|
||
}
|
||
}
|
||
|
||
// —— 截图压缩:把手机原图(可能几 MB)经 canvas 缩到最长边 ≤1280、JPEG ~0.8 转 Blob,避免上传超时 ——
|
||
var _COMPRESS_MAX_EDGE = 1280;
|
||
var _COMPRESS_QUALITY = 0.8;
|
||
function compressImage(file) {
|
||
return new Promise(function (resolve) {
|
||
// 非图片或不支持时,原样回退(字段名仍叫 images)
|
||
if (!file || !file.type || file.type.indexOf('image/') !== 0) { resolve(file); return; }
|
||
var url = URL.createObjectURL(file);
|
||
var img = new Image();
|
||
img.onload = function () {
|
||
try {
|
||
var w = img.naturalWidth || img.width;
|
||
var h = img.naturalHeight || img.height;
|
||
var scale = Math.min(1, _COMPRESS_MAX_EDGE / Math.max(w, h)); // 只缩不放大
|
||
var cw = Math.max(1, Math.round(w * scale));
|
||
var ch = Math.max(1, Math.round(h * scale));
|
||
var canvas = document.createElement('canvas');
|
||
canvas.width = cw; canvas.height = ch;
|
||
canvas.getContext('2d').drawImage(img, 0, 0, cw, ch);
|
||
canvas.toBlob(function (blob) {
|
||
URL.revokeObjectURL(url);
|
||
resolve(blob || file); // toBlob 失败兜底用原 file
|
||
}, 'image/jpeg', _COMPRESS_QUALITY);
|
||
} catch (e) { URL.revokeObjectURL(url); resolve(file); }
|
||
};
|
||
img.onerror = function () { URL.revokeObjectURL(url); resolve(file); }; // 解码失败兜底原 file
|
||
img.src = url;
|
||
});
|
||
}
|
||
|
||
// 提交反馈:真接 POST /api/v1/report(multipart)
|
||
var _submitting = false;
|
||
async function submitFeedback() {
|
||
var btn = document.getElementById('fbSubmitBtn');
|
||
if (btn.disabled || _submitting) return;
|
||
if (!selectedRecord) { showToast('请先选择一条记录'); return; }
|
||
|
||
var priceYuan = parseFloat(document.getElementById('fbPriceInput').value);
|
||
if (!(priceYuan > 0)) { showToast('请填写更低价'); return; }
|
||
|
||
// 校验:上报价(元)必须 < 原最低价(bestPriceCents/100),否则拦下
|
||
var bestYuan = (selectedRecord.bestPriceCents != null) ? selectedRecord.bestPriceCents / 100 : null;
|
||
if (bestYuan != null && !(priceYuan < bestYuan)) {
|
||
showToast('上报价需低于原最低价 ¥' + bestYuan.toFixed(2));
|
||
return;
|
||
}
|
||
|
||
// 平台映射:chip 的 data-pf 已是规范 id,仍走 resolvePlatformId 归一化兜底
|
||
var reportedPlatformId = resolvePlatformId(selectedPlatform) || selectedPlatform;
|
||
if (!reportedPlatformId) { showToast('请选择平台'); return; }
|
||
|
||
if (!selectedFiles || selectedFiles.length === 0) { showToast('请上传截图证明'); return; }
|
||
|
||
// 浏览器预览(无 Bridge):保留占位行为,不真发
|
||
var hasBridge = window.SGBridge && typeof SGBridge.getToken === 'function';
|
||
if (!hasBridge) {
|
||
console.log('[stub] submitFeedback(浏览器预览,无 Bridge,不真发)', {
|
||
comparison_record_id: selectedRecord.id,
|
||
reported_platform_id: reportedPlatformId,
|
||
reported_price: priceYuan,
|
||
images: selectedFiles.length
|
||
});
|
||
closeFeedbackSheet();
|
||
exitSelectMode();
|
||
resetSelectedFiles();
|
||
setTimeout(function () { showToast('感谢您的反馈'); }, 220);
|
||
return;
|
||
}
|
||
|
||
// 真机提交:防重复 + 按钮置灰文案「提交中…」
|
||
_submitting = true;
|
||
var oldText = btn.textContent;
|
||
btn.disabled = true;
|
||
btn.textContent = '提交中…';
|
||
try {
|
||
var token = SGBridge.getToken();
|
||
var base = '';
|
||
|
||
// 逐张压缩
|
||
var blobs = [];
|
||
for (var i = 0; i < selectedFiles.length; i++) {
|
||
blobs.push(await compressImage(selectedFiles[i]));
|
||
}
|
||
|
||
// 组 FormData:字段名按后端约定(reported_price 是「元」,images 逐张 append、字段名都叫 images)
|
||
var fd = new FormData();
|
||
fd.append('comparison_record_id', String(selectedRecord.id));
|
||
fd.append('reported_platform_id', reportedPlatformId);
|
||
fd.append('reported_price', String(priceYuan));
|
||
blobs.forEach(function (b, i) {
|
||
var fname = 'report_' + (i + 1) + '.jpg';
|
||
fd.append('images', b, fname);
|
||
});
|
||
|
||
// fetch:只放 Authorization,不手动设 Content-Type(让浏览器自动带 multipart boundary)
|
||
var res = await fetch(base + '/api/v1/report', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': 'Bearer ' + token },
|
||
body: fd
|
||
});
|
||
|
||
if (res.ok) {
|
||
closeFeedbackSheet();
|
||
exitSelectMode();
|
||
resetSelectedFiles();
|
||
setTimeout(function () { showToast('感谢您的反馈'); }, 220);
|
||
} else {
|
||
var detail = '';
|
||
try { var j = await res.json(); detail = (j && j.detail) ? j.detail : ''; } catch (e) {}
|
||
showToast(detail || ('提交失败(' + res.status + '),请稍后再试'));
|
||
}
|
||
} catch (e) {
|
||
showToast('网络异常,提交失败');
|
||
} finally {
|
||
_submitting = false;
|
||
btn.textContent = oldText;
|
||
refreshSubmitBtn(); // 由三必填条件重新决定按钮可用
|
||
}
|
||
}
|
||
|
||
// ========== 启动:拉数据 → 渲染 ==========
|
||
(async function init(){
|
||
RECORDS = await loadRecords();
|
||
// RECORDS===null 表示未登录(无 token)→ 进未登录态(骨架 + 登录卡)
|
||
setLoggedIn(RECORDS !== null);
|
||
// 从「我的 · 上报与比价」深链进入:登录态下自动进单选上报
|
||
var autoReport = false;
|
||
try { autoReport = new URLSearchParams(location.search).get('report') === '1'; } catch(e) {}
|
||
renderList('');
|
||
if (autoReport && RECORDS !== null) enterSelectMode();
|
||
})();
|
||
</script>
|
||
<script>
|
||
(function() {
|
||
function getTodayKey() {
|
||
var d = new Date();
|
||
return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
|
||
}
|
||
function updateWelfareCheckinTabDot() {
|
||
var tab = document.getElementById('homeTabWelfare');
|
||
if (!tab) return;
|
||
var done = false;
|
||
try { done = localStorage.getItem('proto_checkinDoneDate') === getTodayKey(); } catch(e) {}
|
||
tab.classList.toggle('has-welfare-dot', !done);
|
||
}
|
||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', updateWelfareCheckinTabDot);
|
||
else updateWelfareCheckinTabDot();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|