diff --git a/README.md b/README.md index 621ba48..09cb2a2 100644 --- a/README.md +++ b/README.md @@ -266,7 +266,7 @@ data: {"d": 1} ## 当前限制 -- 订单查询使用可编辑测试副本,尚未接入实时承运商物流轨迹或 MCP;旧历史中的模拟轨迹保留演示标识。 +- 订单查询使用可编辑测试副本,尚未接入实时承运商物流轨迹或 MCP;旧历史卡片继续兼容展示。 - 不预置客服账号;应用内客服登录仍沿用旧账号校验逻辑。客服工作台及接口仍须通过 Nginx 的专用访问密码保护,客户聊天不要求此密码。 - 后台登录尚未建立完整会话鉴权,管理页面和接口缺少登录校验。 - 运费数字由 Python 计算后交给模型组织说明;附加费是否适用仍需结合具体货物和地址核对。 diff --git a/docs/architecture.md b/docs/architecture.md index c522453..d5d9c8c 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -26,7 +26,7 @@ PowerShell 下把目录和 `-g '*.py'` 分开传给 `rg`。报价字典可能整 | 历史接口 | `logistics_chat/history_service.py`:`new_chat`、`get_history`、`get_messages`、`del_history`、`clear_all`;对应 `routes/history.py` | `static/js/chat.js`:`ensureConv`、`loadHistory`、`openConversation` | | 知识检索 | `logistics_chat/knowledge.py`:`knowledge_list`、`search_knowledge` | [knowledge.md](knowledge.md) 与代码同步,程序不读取该文档 | | 运费与附加费 | `logistics_chat/pricing.py`:`RATES`、`parse_shipping_intent`、`dims_to_cm`、`pick_rate`、`calc_freight`、`freight_reply`、`calc_warehouse_card`、`detect_surcharge`、`check_island` | `logistics_chat/rates_extra.py` 为唯一扩展数据源,根目录同名文件仅兼容转发 | -| 卡片协议 | `logistics_chat/tracking.py`:`CARD_PREFIX`、`card_to_text`;旧 `extract_tracking_no`、`build_track` 仅兼容旧导入 | `static/js/chat.js`:`fillTrackCard`、`addTrackCard`;新订单卡片由 `order_data.py` 提供,历史模拟轨迹标为演示数据 | +| 卡片协议 | `logistics_chat/tracking.py`:`CARD_PREFIX`、`card_to_text`;旧 `extract_tracking_no`、`build_track` 仅兼容旧导入 | `static/js/chat.js`:`fillTrackCard`、`addTrackCard`;新订单卡片由 `order_data.py` 提供,历史卡片保留兼容渲染 | | 订单测试表 | `logistics_chat/order_data.py`:`init_order_table`、`import_records`、`lookup_order`、`answer_query` | SQLite `order_test_data` 为可编辑快照;按订单号、入仓码、Shipment ID 精确查表。将来 MCP 在取数层替换 `lookup_order`,不让模型拼 SQL | | AI 与 SSE | `logistics_chat/chat_service.py`:`create_chat_stream`、`sse`;`ai_client.py`:`post_completion`、`compress_answer`、`gen_followups` | `routes/chat.py`:`stream_chat`;客户端 `doSend`、`renderError`、`renderFollowups` | | 人工会话 | `logistics_chat/human_service.py`:`human_*`(含转接、备注、撤回);对应 `routes/human.py` | 输入状态在 `app.extensions['human_typing']`;客户 `startHumanPoll`;客服 `loadQueue`、`poll`、`sendReply`;人工申请每天 8:00–22:00 | @@ -87,6 +87,7 @@ tests/ # 按业务模块组织的回归 - 限流、输入状态由应用实例持有,不能每请求重置或跨测试应用共享。维护 CLI 和纯规则可用标准库加载,包入口延迟导入 Web 依赖。 - 单文件围绕一个职责;超过约 300–400 行时检查职责,而非按行数机械拆分。报价数据保持单一来源;兼容转发不复制字典。页面脚本按页面保留既有执行顺序和全局函数,不擅自改为 ES Modules。 - 配色、字体基线和通用控件放在 `theme.css`,页面布局放在对应 CSS。客户页通过 `body.dark` 覆盖主题令牌;新增可翻译文案加入 `chat.js` 的 `I18N`,静态文案用 `data-i18n`。调整样式后检查桌面、390px 窄屏、侧栏、聊天卡片和客服空/活动状态。 +- 客户界面不附加测试/演示声明、底部免责声明、键盘说明、快捷入口副标题或附件准备说明;草稿恢复和发送成功静默处理。加载、录音、人工排队状态及操作失败反馈保留,权限校验继续由服务端执行。 - 客户附件预览由 `clearAttachment` 管理,轨迹展开由 `fillTrackCard` 管理。客服 `drafts` 按 `sid` 保存草稿,发送成功后只清理已确认发送的内容;常用回复仅填入草稿。`renderList` 只在展示数据变化时重绘。 - 客户文字草稿由 `drafts.js` 的 `ChatDrafts.create` 管理,脚本在 `visitor.js` 之后、`chat.js` 之前加载;localStorage 按访客 key 和会话分条存储,sessionStorage 仅记录当前标签页的活动会话。切换前同步保存,输入防抖及页面隐藏时保存;无持久存储时使用页面内存并提示。恢复历史的异步结果受会话版本约束,加载失败禁止继续发送,删除会话同步清除其草稿。草稿不含未发送附件,不跨设备同步。 - 人工申请由服务端以 UTC+8 判断每日 08:00–22:00 营业状态,客户手机时区不参与判断。附件清理保留 `messages` 和 `human_messages` 引用的文件,仅删除超过 7 天的未引用临时文件;读取引用失败时停止清理。附件不存在或不属于当前游客均返回相同的私有、不可缓存 404 页面,破图在聊天中显示可读提示。 diff --git a/logistics_chat/history_service.py b/logistics_chat/history_service.py index 73b7271..a30e6b9 100644 --- a/logistics_chat/history_service.py +++ b/logistics_chat/history_service.py @@ -4,7 +4,7 @@ from .db import connect, now_str from .visitor_service import visitor_id, owns_conversation -WELCOME_TEXT = '您好!欢迎咨询柏悦物流智能客服,可以咨询运费、时效或联系人工客服。订单查询将在客户账号登录并关联后开放。' +WELCOME_TEXT = '您好!欢迎咨询柏悦物流智能客服,可以咨询运费、时效或联系人工客服。' def new_chat(): diff --git a/static/css/chat.css b/static/css/chat.css index 9735a2a..2eba2f0 100644 --- a/static/css/chat.css +++ b/static/css/chat.css @@ -42,7 +42,6 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } .sidebar-footer { border-top: 1px solid var(--border); padding: 15px 7px 0; margin-top: 13px; white-space: nowrap; } .clear-all-btn { border: 0; color: var(--muted); background: transparent; padding: 5px 0; font-size: 11px; } .clear-all-btn:hover { color: var(--danger); } -.sidebar-note { display: flex; align-items: center; gap: 7px; margin-top: 10px; color: var(--muted); font-size: 10px; } .left-panel-hide { width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; visibility: hidden; border: 0; } .main-chat { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; position: relative; overflow: hidden; } .chat-title { flex-shrink: 0; min-height: 75px; padding: 16px 26px; display: flex; align-items: center; gap: 13px; border-bottom: 1px solid var(--border); background: var(--surface); z-index: 31; } @@ -64,7 +63,6 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } .ai-avatar svg { width: 27px; height: 27px; } .welcome-eyebrow { color: var(--brand-text); font-size: 11px; font-weight: 600; letter-spacing: .06em; margin-bottom: 9px; } .ai-welcome-card h3 { font-size: clamp(22px, 2.2vw, 30px); line-height: 1.4; letter-spacing: -.035em; font-weight: 650; color: var(--text); } -.ai-welcome-card p { color: var(--secondary); font-size: 13px; line-height: 1.8; margin-top: 10px; } .suggest-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 28px 0 0; } .suggest-item { display: flex; align-items: center; gap: 13px; min-height: 83px; padding: 17px; border: 1px solid var(--border); border-radius: 11px; background: var(--surface); color: var(--text); text-align: left; width: 100%; } .suggest-item:hover { border-color: #9ab6f5; background: var(--brand-soft); box-shadow: 0 3px 12px rgba(49, 95, 213, .04); } @@ -72,7 +70,6 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } .suggest-icon svg { width: 19px; height: 19px; } .suggest-copy { min-width: 0; } .suggest-copy strong { display: block; font-size: 14px; font-weight: 600; } -.suggest-copy small { display: block; color: var(--secondary); font-size: 12px; margin-top: 4px; line-height: 1.6; } .suggest-arrow { margin-left: auto; color: var(--muted); font-size: 17px; } .hot-questions { margin-top: 24px; } .hot-questions h4, .recent-chat h4 { color: var(--secondary); font-size: 12px; font-weight: 600; margin-bottom: 10px; } @@ -97,7 +94,6 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } .hot-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 10px; } .human-session { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; margin-bottom: 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--brand-soft); color: var(--secondary); font-size: 12px; } .human-session button { flex-shrink: 0; padding: 6px 9px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--brand-text); font-size: 12px; } -.hot-bar-label { font-size: 10px; color: var(--muted); margin-right: 3px; } .hot-bar .hot-capsule { border: 0; padding: 4px 8px; background: var(--surface-soft); font-size: 10px; } .input-area { display: flex; align-items: flex-end; gap: 10px; border: 1px solid var(--border); border-radius: 12px; padding: 9px; background: var(--surface); box-shadow: 0 3px 15px rgba(28, 47, 80, .035); } .input-area:focus-within { border-color: #7f9fe8; box-shadow: 0 0 0 3px var(--brand-soft); } @@ -114,9 +110,6 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } #send-button { min-width: 66px; height: 38px; flex-shrink: 0; padding: 0 15px; border: 0; border-radius: 8px; background: var(--brand); color: white; font-size: 12px; font-weight: 600; } #send-button:hover { background: var(--brand-hover); } #send-button.stop { background: var(--danger); } -.composer-footer { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 9px 2px 0; } -.disclaimer, .keyboard-hint { color: var(--muted); font-size: 11px; } -.keyboard-hint { white-space: nowrap; } .msg-row { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; max-width: 820px; margin: 0 auto 24px; } .msg-row.user-line { align-items: flex-end; } .message-author { display: flex; align-items: center; gap: 8px; min-height: 24px; margin-bottom: 4px; color: var(--secondary); font-size: 12px; font-weight: 600; } @@ -162,7 +155,6 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } .track-expand { width: 100%; margin-top: 3px; padding: 7px 10px; border: 0; border-radius: 7px; background: var(--surface-soft); color: var(--brand-text); font-size: 12px; } .track-expand:hover { background: var(--brand-soft); } .track-card .msg-actions { margin-top: 8px; padding-top: 8px; } -.order-data-notice { margin: 12px 0; padding: 10px; border-radius: 7px; background: var(--brand-soft); color: var(--secondary); font-size: 12px; line-height: 1.8; } .order-data-fields { display: grid; grid-template-columns: minmax(90px, auto) minmax(0, 1fr); gap: 7px 16px; margin: 14px 0; font-size: 13px; } .order-data-fields dt { color: var(--muted); } .order-data-fields dd { margin: 0; overflow-wrap: anywhere; } @@ -180,7 +172,6 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } .attachment-file-icon { display: grid; place-items: center; flex-shrink: 0; width: 42px; height: 42px; font-size: 10px; font-weight: 650; border-radius: 7px; background: var(--brand-soft); color: var(--brand-text); } .attachment-copy { flex: 1; min-width: 0; } .attachment-copy strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 500; } -.attachment-copy small { display: block; font-size: 10px; color: var(--muted); margin-top: 2px; } .attachment-remove { flex-shrink: 0; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 7px; color: var(--secondary); background: transparent; font-size: 21px; } .attachment-remove:hover { color: var(--danger); background: var(--danger-soft); } .composer-status { font-size: 12px; color: var(--secondary); padding: 7px 3px 0; } @@ -192,7 +183,6 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } .ai-welcome-card { padding-top: 0; } .ai-avatar { width: 42px; height: 42px; margin-bottom: 12px; } .welcome-eyebrow { margin-bottom: 6px; } - .ai-welcome-card p { margin-top: 8px; } .suggest-grid { margin-top: 20px; gap: 10px; } .suggest-item { min-height: 72px; padding: 13px 16px; } .hot-questions { margin-top: 18px; } @@ -205,7 +195,6 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } .composer { width: calc(100% - 50px); } .service-label { display: none; } .suggest-item { padding: 13px; gap: 10px; } - .suggest-copy small { font-size: 11px; } } @media (max-width: 900px) { body { padding: 0; position: fixed; inset: 0; width: 100%; overscroll-behavior: none; } @@ -250,12 +239,10 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } .ai-avatar { display: none; } .ai-welcome-card { padding-top: 12px; } .ai-welcome-card h3 { font-size: 23px; } - .ai-welcome-card p { max-width: 300px; margin: 10px auto 0; font-size: 12px; } .suggest-grid { gap: 9px; margin-top: 22px; } - .suggest-item { flex-direction: column; align-items: flex-start; justify-content: flex-start; min-height: 114px; padding: 13px; gap: 9px; } + .suggest-item { flex-direction: column; align-items: flex-start; justify-content: flex-start; min-height: 88px; padding: 13px; gap: 9px; } .suggest-icon { width: 32px; height: 32px; border-radius: 8px; } .suggest-copy strong { font-size: 13px; } - .suggest-copy small { font-size: 11px; } .suggest-arrow { display: none; } .hot-questions { margin-top: 22px; } .hot-questions .hot-capsule-wrap { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding-bottom: 3px; } @@ -264,16 +251,12 @@ body { padding: 20px; height: 100vh; height: 100dvh; overflow: hidden; } #recentChatWrap { grid-template-columns: 1fr; gap: 0; } .composer { width: calc(100% - 24px); padding-bottom: calc(10px + env(safe-area-inset-bottom)); } .hot-bar { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; gap: 5px; } - .hot-bar-label { display: none; } .hot-bar .hot-capsule { white-space: nowrap; flex-shrink: 0; padding: 6px 9px; font-size: 11px; } .input-area { gap: 7px; padding: 7px; } #send-button { min-width: 57px; padding: 0 12px; } #upload-btn { flex-basis: 36px; } #input-text { font-size: 16px; } .attachment-remove { width: 40px; height: 40px; } - .keyboard-hint { display: none; } - .composer-footer { justify-content: center; padding-top: 7px; } - .disclaimer { font-size: 10px; } .track-text { grid-template-columns: 1fr; gap: 2px; } .track-place { grid-column: 1; } .robot-message.track-card { padding: 16px; } diff --git a/static/js/chat.js b/static/js/chat.js index 7b72c7d..2cdb129 100644 --- a/static/js/chat.js +++ b/static/js/chat.js @@ -142,9 +142,7 @@ uploadInput.onchange = function(){ copy.className = "attachment-copy"; const name = document.createElement("strong"); name.textContent = data.name; - const hint = document.createElement("small"); - hint.textContent = t("attachmentReady"); - copy.append(name, hint); + copy.append(name); const remove = document.createElement("button"); remove.type = "button"; remove.className = "attachment-remove"; @@ -240,7 +238,7 @@ const I18N = { thinking: "正在思考中...", busyRetry: "服务繁忙,请稍后重试", sessionFail: "会话创建失败,请确认服务已启动", - autoWelcome: "您好!欢迎咨询柏悦物流智能客服,可以咨询运费、时效或联系人工客服。订单查询将在客户账号登录并关联后开放。", + autoWelcome: "您好!欢迎咨询柏悦物流智能客服,可以咨询运费、时效或联系人工客服。", humanHours: "人工客服工作时间:每天北京时间 8:00-22:00。当前为非工作时间,请留言,工作时间会尽快回复您。", offlinePrompt: "请输入您的留言:", offlineSaved: "留言已提交,工作时间(8:00-22:00)会尽快回复您。", @@ -250,7 +248,6 @@ const I18N = { humanWaiting: "正在排队,消息将由人工客服接入后回复。您也可以返回 AI 继续咨询。", humanActive: "人工客服接待中,消息将发送给人工客服。", returnAI: "返回 AI 问答", - humanSent: "已发送给人工客服", humanSending: "正在发送给人工客服…", humanSendFailed: "消息未发送成功,内容已保留,请重试。", humanConnectFailed: "人工客服连接失败,请稍后重试。", @@ -258,7 +255,6 @@ const I18N = { humanReturnFailed: "暂时无法结束人工会话,请重试。", historyLoading: "正在加载这段会话,请稍候…", historyLoadFailed: "会话未加载成功,请重新选择该会话重试,或新建对话。", - draftRestored: "已恢复这段会话的未发送草稿。", draftSaveFailed: "草稿暂时无法保存到当前浏览器,刷新前请保留输入内容。", attachmentUnavailable: "图片暂时无法访问,请重新发送附件。", recalled: "该消息已撤回", @@ -268,7 +264,7 @@ const I18N = { networkFail: "网络连接中断,请检查网络后重试", stopped: "已停止生成", stoppedMark: "(已停止生成)", - inputPh: "请输入问题、订单号或入仓码…(Enter 发送,Shift+Enter 换行)", + inputPh: "输入问题、订单号或入仓码…", send: "发送", stop: "停止", copy: "复制", @@ -280,17 +276,12 @@ const I18N = { retryAIOnly: "请先返回 AI 问答,再重答这条消息。", clickRetry: "点击重试", welcomeTitle: "物流问题,随时为您解答", - welcomeSub: "咨询运费、时效或联系人工客服。订单查询需关联客户账号。", welcomeEyebrow: "中欧 · 英国物流服务", - serviceScope: "中欧与英国专线咨询", serviceTag: "物流咨询服务", - quickLabel: "快捷咨询", footerTrack: "查订单", footerQuote: "专线报价", footerException: "异常处理", footerHuman: "联系人工", - disclaimer: "报价与时效仅供参考,请以客服最终确认为准", - keyboardHint: "Enter 发送 · Shift + Enter 换行", inputShort: "输入问题…", uploading: "附件正在上传,请稍候…", uploadFailed: "附件上传失败,请重新选择文件", @@ -307,7 +298,6 @@ const I18N = { voiceEmpty: "没有识别到文字,请靠近麦克风重试。", voiceFailed: "语音输入失败,请重试或使用文字输入。", voiceTimeout: "语音处理超时,请重试。", voiceWait: "请等待当前操作完成后,再使用语音输入。", voiceDraft: "输入框还有未发送内容,请先发送或清空后再使用语音。", - attachmentReady: "已准备好,发送后将附在消息中", removeAttachment: "移除待发送附件", you: "您", assistant: "柏悦物流助手", @@ -321,15 +311,12 @@ const I18N = { logisticsHistory: "物流轨迹", noLogisticsHistory: "暂无已发生的物流节点", estimatedTracking: "预计节点", - legacyTrackingNotice: "历史模拟轨迹,仅供演示,不代表真实订单。", order_inbound: "入仓码", order_shipment: "Shipment ID", order_country: "目的国", order_warehouse: "目的仓", order_transport: "运输方式", order_packages: "件数", order_weight: "申报毛重", order_volume: "申报体积", order_role: "订单层级", order_parent: "父订单号", order_updated: "来源更新时间", moreTracking: "展开全部轨迹", lessTracking: "收起较早轨迹", - quickSubs: ["关联客户账号后查询订单与轨迹", "了解线路、费用与预计时效", "咨询延误、签收或理赔问题", "进一步沟通您的物流需求"], - welcomeTip: "您可以直接输入问题,也可以点击下面选项快速提问。", hot: "热点问题", recent: "最近对话记录", sideCollapse: "收起侧栏", @@ -357,7 +344,7 @@ const I18N = { thinking: "Thinking...", busyRetry: "Service busy, please try again later", sessionFail: "Failed to create session, please check the service is running", - autoWelcome: "Welcome to Baiyue Logistics. Ask about rates, transit times or human support. Order lookup will be available after signing in and linking a customer account.", + autoWelcome: "Welcome to Baiyue Logistics. Ask about rates, transit times or human support.", humanHours: "Our team is available daily, 08:00–22:00 China Standard Time (UTC+8). Please leave a message and we will reply during service hours.", offlinePrompt: "Please leave your message:", offlineSaved: "Message submitted. Our team will reply during service hours (08:00–22:00).", @@ -367,7 +354,6 @@ const I18N = { humanWaiting: "You are in the queue. A team member will reply after joining, or you can return to AI chat.", humanActive: "You are chatting with our support team. Messages will be sent to a team member.", returnAI: "Return to AI chat", - humanSent: "Sent to our support team", humanSending: "Sending to our support team…", humanSendFailed: "Message not sent. Your text has been kept. Please retry.", humanConnectFailed: "Could not connect to our support team. Please try again.", @@ -375,7 +361,6 @@ const I18N = { humanReturnFailed: "Could not end the support session. Please try again.", historyLoading: "Loading this conversation. Please wait…", historyLoadFailed: "Conversation could not load. Select it again to retry, or start a new chat.", - draftRestored: "Your unsent draft has been restored.", draftSaveFailed: "Your browser could not save this draft. Keep a copy before refreshing.", attachmentUnavailable: "Image unavailable. Please upload the attachment again.", recalled: "This message was recalled", @@ -385,7 +370,7 @@ const I18N = { networkFail: "Connection lost, please check your network and retry", stopped: "Stopped", stoppedMark: "(Generation stopped)", - inputPh: "Ask a question or enter a tracking number... (Enter to send, Shift+Enter for newline)", + inputPh: "Ask a question or enter an order number…", send: "Send", stop: "Stop", copy: "Copy", @@ -397,17 +382,12 @@ const I18N = { retryAIOnly: "Return to AI chat before regenerating this answer.", clickRetry: "Retry", welcomeTitle: "Your next shipment, made clearer.", - welcomeSub: "Ask about rates, transit times or human support. Order lookup requires a linked customer account.", welcomeEyebrow: "EUROPE & UK LOGISTICS", - serviceScope: "Europe & UK freight support", serviceTag: "Logistics support", - quickLabel: "Quick help", footerTrack: "Order lookup", footerQuote: "Get a quote", footerException: "Parcel issue", footerHuman: "Human support", - disclaimer: "Quotes and transit times are subject to final confirmation", - keyboardHint: "Enter to send · Shift + Enter for a new line", inputShort: "Message…", uploading: "Uploading your attachment…", uploadFailed: "Upload failed. Please select the file again.", @@ -426,7 +406,6 @@ const I18N = { voiceFailed: "Voice input failed. Please try again or type your message.", voiceTimeout: "Voice input timed out. Please try again.", voiceWait: "Please wait for the current operation to finish before recording.", voiceDraft: "Please send or clear your existing draft before recording.", - attachmentReady: "Ready to attach to your next message", removeAttachment: "Remove pending attachment", you: "You", assistant: "Baiyue Assistant", @@ -440,15 +419,12 @@ const I18N = { logisticsHistory: "Shipment tracking", noLogisticsHistory: "No actual shipment events available", estimatedTracking: "Estimated milestones", - legacyTrackingNotice: "Historical simulated tracking for demonstration only; this is not a real order record.", order_inbound: "Inbound code", order_shipment: "Shipment ID", order_country: "Destination country", order_warehouse: "Warehouse", order_transport: "Transport", order_packages: "Packages", order_weight: "Declared gross weight", order_volume: "Declared volume", order_role: "Order level", order_parent: "Parent order", order_updated: "Source updated", moreTracking: "View full tracking history", lessTracking: "Show recent updates", - quickSubs: ["Link a customer account to view orders and tracking", "Explore routes, rates and transit times", "Get help with delays, delivery or claims", "Discuss your shipping requirements"], - welcomeTip: "Type your question, or pick a quick option below.", hot: "Popular Questions", recent: "Recent Conversations", sideCollapse: "Collapse sidebar", @@ -629,7 +605,7 @@ function welcomeHTML(){ const kinds = ["track", "quote", "issue", "human"]; const cards = q.map(function(question, index){ const action = index === 3 ? 'data-human-support' : `data-question="${escHtml(question)}"`; - return ``; + return ``; }).join(""); return `
${t("welcomeSub")}
咨询运费、时效或联系人工客服。
订单查询需登录并关联客户账号。