""" 附件询价 H5 上传页。 本文件职责:渲染选文件页与过期/成功/拒收提示。URL 不含 userid。 样式按企微内嵌、手机竖屏:卡片、虚线选区、已选文件列表;不改上传规则。 """ from __future__ import annotations from html import escape import json from agent.channel.h5.wecom_close import wecom_auto_close_script from agent.policy import inquiry_copy as copy # 三页共用:浅底、白卡片、企微蓝主按钮。禁止在页面里写 userid。 _PAGE_CSS = """ :root { --bg: #eef3f9; --card: #ffffff; --text: #17233d; --muted: #5b6b7c; --line: #c5d4ea; --brand: #1677ff; --brand-press: #0f62d9; --danger: #d83931; --ok: #0f8a5f; --shadow: 0 10px 28px rgba(23, 51, 91, 0.10); } * { box-sizing: border-box; } html, body { margin: 0; min-height: 100%; } body { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif; background: var(--bg); color: var(--text); -webkit-font-smoothing: antialiased; } .wrap { max-width: 480px; margin: 0 auto; padding: 20px 16px calc(24px + env(safe-area-inset-bottom)); } .card { background: var(--card); border-radius: 16px; padding: 22px 20px 24px; box-shadow: var(--shadow); } .hero { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 12px; } .badge { width: 44px; height: 44px; border-radius: 12px; background: #e8f1ff; color: var(--brand); display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .badge svg { width: 22px; height: 22px; } h1 { font-size: 20px; line-height: 1.3; margin: 0 0 4px; font-weight: 600; } .lead { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.6; } .hint { margin: 12px 0 0; color: var(--muted); font-size: 13px; line-height: 1.7; } .pills { display: flex; gap: 8px; margin: 14px 0 0; } .pill { font-size: 12px; color: #2b5aa8; background: #eef4ff; border-radius: 999px; padding: 4px 10px; } .drop { position: relative; display: block; margin-top: 18px; padding: 22px 16px; border: 1.5px dashed var(--line); border-radius: 12px; background: #f7faff; text-align: center; cursor: pointer; -webkit-tap-highlight-color: transparent; } .drop:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(22, 119, 255, 0.16); } .drop input { position: absolute; top: 0; right: 0; bottom: 0; left: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer; } .drop-title { display: block; font-size: 16px; font-weight: 600; } .drop-sub { display: block; margin-top: 6px; font-size: 13px; color: var(--muted); } .picked { list-style: none; margin: 14px 0 0; padding: 0; } .picked li { display: flex; align-items: center; gap: 10px; padding: 10px 12px; margin-top: 8px; background: #f6f8fb; border-radius: 10px; font-size: 14px; } .idx { width: 22px; height: 22px; border-radius: 50%; background: #e8f1ff; color: var(--brand); font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .name { word-break: break-all; } button[type="submit"], .btn { width: 100%; min-height: 48px; margin-top: 20px; border: 0; border-radius: 10px; background: var(--brand); color: #fff; font-size: 16px; font-weight: 700; cursor: pointer; touch-action: manipulation; } button[type="submit"]:active, .btn:active { background: var(--brand-press); } button[disabled] { background: #94a3b8; cursor: not-allowed; } .err { min-height: 1.4em; margin: 12px 0 0; color: var(--danger); font-size: 14px; line-height: 1.5; } .rules { margin: 16px 0 0; padding-left: 18px; color: var(--muted); font-size: 13px; line-height: 1.8; } .tone-ok .badge { background: #e7f7ef; color: var(--ok); } .tone-warn .badge { background: #fdecea; color: var(--danger); } .tone-expired .badge { background: #eef1f5; color: var(--muted); } .body { margin: 8px 0 0; font-size: 15px; line-height: 1.7; } .back { margin: 10px 0 0; color: var(--muted); font-size: 14px; line-height: 1.6; } @media (prefers-reduced-motion: reduce) { * { transition: none !important; } } """ _ICON_FILE = ( '' ) _ICON_OK = ( '' ) _ICON_WARN = ( '' ) _ICON_EXPIRED = ( '' ) def _shell(*, title: str, body: str, tone: str = "ok") -> str: """成功 / 拒绝 / 过期共用卡片壳,避免三页三种简陋白底。""" icons = {"ok": _ICON_OK, "warn": _ICON_WARN, "expired": _ICON_EXPIRED} icon = icons.get(tone, _ICON_WARN) extra = '

正在返回企微,请查看识别结果。

' if tone == "ok" else "" closer = wecom_auto_close_script() if tone == "ok" else "" return f""" {title}
{icon}

{title}

{body}

{extra}
{closer} """ def render_upload_page() -> str: """上传页:一次可选多个 Excel/PDF;图片请发回企微。""" reject = json.dumps(copy.ATTACH_H5_REJECT_TYPE, ensure_ascii=False) need = json.dumps(copy.ATTACH_H5_NEED_FILE, ensure_ascii=False) return f""" 上传询价附件
{_ICON_FILE}

上传询价附件

这里只传 Excel、PDF。图片请直接发到当前企微会话。

ExcelPDF

一次可选多个文件,顺序按你选中的顺序。提交后请回企微查看识别结果。

""" def render_upload_message(title: str, body: str, *, expired: bool = False) -> str: """成功 / 拒绝 / 过期页。""" tone = "expired" if expired else ("ok" if title == "上传成功" else "warn") return _shell(title=escape(title), body=escape(body), tone=tone) def expired_upload_page() -> str: return render_upload_message("链接已过期", copy.attach_h5_expired(), expired=True)