From 6d629181ce18f5212d01375b8b5b76970aa33c60 Mon Sep 17 00:00:00 2001 From: zoujing Date: Mon, 27 Jul 2026 11:42:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E4=BA=86=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E7=9A=84=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../queue/components/TicketCard.vue | 26 ++++++------ src/pages-base/queue/index.vue | 42 +++++++++---------- src/pages-base/queue/request/api/QueueApi.js | 14 +------ src/pages-base/queue/result.vue | 3 +- src/pages-base/queue/utils/queueUtils.mjs | 25 ++++------- 5 files changed, 46 insertions(+), 64 deletions(-) diff --git a/src/pages-base/queue/components/TicketCard.vue b/src/pages-base/queue/components/TicketCard.vue index 60f91f9..da26fd7 100644 --- a/src/pages-base/queue/components/TicketCard.vue +++ b/src/pages-base/queue/components/TicketCard.vue @@ -3,7 +3,7 @@ - {{ ticket.project_name }} + {{ ticket.projectName }} {{ ticketLabel }} @@ -11,24 +11,24 @@ 我的号码 - {{ ticket.ticket_number }} - 本号 {{ ticket.party_size }} 人 + {{ ticket.ticketNumber }} + 本号 {{ ticket.partySize }} 人 当前最新到号 - {{ ticket.latest_called_number || "暂无" }} + {{ ticket.latestCalledNumber || "暂无" }} @@ -41,7 +41,7 @@ 现在请前往 {{ ticket.entrance || "现场入口" }} - 请出示排队号码,叫号时间 {{ formatDateTime(ticket.called_at) }} + 请出示排队号码,叫号时间 {{ formatDateTime(ticket.calledAt) }} @@ -51,7 +51,7 @@ @@ -78,13 +78,13 @@ const props = defineProps({ }); const ticketStatus = computed(() => String(props.ticket?.status || "").toUpperCase()); -const statusMeta = computed(() => visitorStatusMeta(props.ticket.status, props.ticket.people_ahead)); +const statusMeta = computed(() => visitorStatusMeta(props.ticket.status, props.ticket.peopleAhead)); const projectPaused = computed(() => ticketStatus.value === "WAITING" && String(props.ticket?.project?.status || "").toUpperCase() === "PAUSED"); const ticketTone = computed(() => projectPaused.value ? "warning" : statusMeta.value.tone); const ticketLabel = computed(() => projectPaused.value ? "项目暂时暂停" : statusMeta.value.label); -const phoneLast4 = computed(() => String(props.ticket?.phone_last4 || "").replace(/\D/g, "").slice(-4) || "未知"); +const phoneLast4 = computed(() => String(props.ticket?.phoneLast4 || "").replace(/\D/g, "").slice(-4) || "未知"); const visitorNotice = computed(() => { - if (props.ticket?.visitor_notice === null) return ""; - return props.ticket?.visitor_notice?.trim() || "请您在景区附近等候,注意听从工作人员指引。"; + if (props.ticket?.visitorNotice === null) return ""; + return props.ticket?.visitorNotice?.trim() || "请您在景区附近等候,注意听从工作人员指引。"; }); diff --git a/src/pages-base/queue/index.vue b/src/pages-base/queue/index.vue index 1194d2b..5d58e53 100644 --- a/src/pages-base/queue/index.vue +++ b/src/pages-base/queue/index.vue @@ -87,18 +87,18 @@ - {{ selectedProject.visitor_notice }} + {{ selectedProject.visitorNotice }} @@ -120,9 +120,9 @@ @@ -216,7 +216,7 @@ :show-retry="Boolean(currentStatusResource.error && currentStatusResource.data)" @retry="refreshCurrentStatus" /> - + @@ -284,9 +284,9 @@ const createIntent = ref(null); const ticketForm = reactive({ phone: "", - last_name: "", + lastName: "", honorific: "", - party_size: 1, + partySize: 1, }); const projectsResource = reactive({ @@ -319,11 +319,11 @@ const statusRequestGate = createSingleFlight(); const selectedProject = computed(() => projectsResource.projects.find((project) => project.id === selectedProjectId.value) || null); const projectNames = computed(() => projectsResource.projects.map((project) => project.name)); const selectedProjectIndex = computed(() => Math.max(0, projectsResource.projects.findIndex((project) => project.id === selectedProjectId.value))); -const minPartySize = computed(() => Number(selectedProject.value?.min_party_size) || 1); -const maxPartySize = computed(() => Number(selectedProject.value?.max_party_size) || minPartySize.value); +const minPartySize = computed(() => Number(selectedProject.value?.minPartySize) || 1); +const maxPartySize = computed(() => Number(selectedProject.value?.maxPartySize) || minPartySize.value); const canSubmitTicket = computed(() => !createBusy.value && Boolean(selectedProjectId.value) && Boolean(projectsResource.projects.length)); const createdTicketTarget = computed(() => createdTicket.value ? resolveStatusTarget(createdTicket.value) : null); -const createdTicketProjectName = computed(() => selectedProject.value?.name || createdTicket.value?.ticket?.project_name || "当前项目"); +const createdTicketProjectName = computed(() => selectedProject.value?.name || createdTicket.value?.ticket?.projectName || "当前项目"); const currentStatusResource = computed(() => tokenResource); const currentStale = computed(() => Boolean(currentStatusResource.value.data) && isTimestampStale(currentStatusResource.value.lastSuccessAt, 30_000)); const headerTone = computed(() => { @@ -356,7 +356,7 @@ watch(selectedProject, () => normalizePartySize()); onLoad((options = {}) => { const sceneToken = extractVisitorToken(parseRouteValue(options.scene)); - const token = parseRouteValue(options.token || options.public_token) || sceneToken; + const token = parseRouteValue(options.token || options.publicToken) || sceneToken; const phone = parseRouteValue(options.phone); const tab = parseRouteValue(options.tab); @@ -504,9 +504,9 @@ const handleProjectChange = (event) => { const normalizePartySize = () => { if (!selectedProject.value) return; - const current = Number(ticketForm.party_size); + const current = Number(ticketForm.partySize); if (!Number.isInteger(current) || current < minPartySize.value || current > maxPartySize.value) { - ticketForm.party_size = minPartySize.value; + ticketForm.partySize = minPartySize.value; } } @@ -516,9 +516,9 @@ const handleTicketFormInput = (field, event) => { const updateTicketForm = (field, value) => { let nextValue = value; - if (field === "party_size") { + if (field === "partySize") { const digits = digitsOnly(value); - ticketForm.party_size = digits === "" ? "" : Number(digits); + ticketForm.partySize = digits === "" ? "" : Number(digits); nextValue = digits; } else if (field === "phone") { const phone = digitsOnly(value); @@ -560,7 +560,7 @@ const submitTicket = async () => { return; } - const partySize = Number(ticketForm.party_size); + const partySize = Number(ticketForm.partySize); if (!Number.isInteger(partySize) || partySize < minPartySize.value || partySize > maxPartySize.value) { formError.value = `本项目每个号码可绑定 ${minPartySize.value} 到 ${maxPartySize.value} 人`; return; @@ -576,7 +576,7 @@ const submitTicket = async () => { const payload = { phone, party_size: partySize, - last_name: ticketForm.last_name.trim(), + last_name: ticketForm.lastName.trim(), honorific: ticketForm.honorific, allow_duplicate: confirmDuplicatePhone.value, }; @@ -612,9 +612,9 @@ const submitTicket = async () => { const resetTicketForm = () => { ticketForm.phone = ""; - ticketForm.last_name = ""; + ticketForm.lastName = ""; ticketForm.honorific = ""; - ticketForm.party_size = minPartySize.value; + ticketForm.partySize = minPartySize.value; phoneError.value = ""; formError.value = ""; } @@ -700,7 +700,7 @@ const refreshTokenStatus = async ({ initial = false } = {}) => { try { const data = await getPublicStatus(currentToken.value); if (requestId !== statusRequestId || viewMode.value !== VIEW_TOKEN) return; - applyStatusSuccess(tokenResource, data); + applyStatusSuccess(tokenResource, Array.isArray(data?.tickets) ? data.tickets[0] : data); } catch (error) { if (requestId !== statusRequestId || viewMode.value !== VIEW_TOKEN) return; applyStatusError(tokenResource, error); diff --git a/src/pages-base/queue/request/api/QueueApi.js b/src/pages-base/queue/request/api/QueueApi.js index c19f61f..58c06ef 100644 --- a/src/pages-base/queue/request/api/QueueApi.js +++ b/src/pages-base/queue/request/api/QueueApi.js @@ -1,4 +1,3 @@ -import { normalizePublicStatus } from "../../utils/queueUtils.mjs"; import request from "../base/request"; export class QueueApiError extends Error { @@ -74,22 +73,13 @@ export const createPublicTicket = (projectId, payload, idempotencyKey = newQueue project_id: projectId, idempotency_key: idempotencyKey, ...payload, - })).then((response) => ({ - ...response, - ticket: normalizePublicStatus(response.ticket), })); } export const getPublicStatus = () => { - return normalizeQueueRequest(request.get("/hotelBiz/visitorQueue/status", {})) - .then((response) => normalizePublicStatus(response)); + return normalizeQueueRequest(request.get("/hotelBiz/visitorQueue/status", {})); } export const searchPublicStatusByPhone = () => { - return normalizeQueueRequest(request.get("/hotelBiz/visitorQueue/status", {})).then((response) => { - if (Array.isArray(response.tickets)) { - return { tickets: response.tickets.map(normalizePublicStatus) }; - } - return { tickets: [normalizePublicStatus(response)] }; - }); + return getPublicStatus(); } diff --git a/src/pages-base/queue/result.vue b/src/pages-base/queue/result.vue index 366f5de..d3ecf46 100644 --- a/src/pages-base/queue/result.vue +++ b/src/pages-base/queue/result.vue @@ -118,7 +118,6 @@ let requestId = 0; const hasLookupTarget = computed(() => Boolean(phone.value || token.value)); const tickets = computed(() => { - if (token.value) return resource.data ? [resource.data] : []; return resource.data?.tickets || []; }); const selectedTicket = computed(() => { @@ -153,7 +152,7 @@ watch(tickets, () => syncSelectedTicket(), { deep: true }); onLoad((options = {}) => { phone.value = parseRouteValue(options.phone); - token.value = parseRouteValue(options.token || options.public_token); + token.value = parseRouteValue(options.token || options.publicToken); }); onShow(() => { diff --git a/src/pages-base/queue/utils/queueUtils.mjs b/src/pages-base/queue/utils/queueUtils.mjs index ee14546..7e5b7da 100644 --- a/src/pages-base/queue/utils/queueUtils.mjs +++ b/src/pages-base/queue/utils/queueUtils.mjs @@ -26,11 +26,13 @@ export const formatEstimatedWait = (value) => { return Number.isFinite(numeric) ? `约 ${Math.max(0, Math.round(numeric))} 分钟` : value; } if (typeof value === "object") { - const estimate = Number(value.estimate_minutes); + if (value.available === false && value.reason) return value.reason; + + const estimate = Number(value.estimateMinutes); if (Number.isFinite(estimate)) return `约 ${Math.max(0, Math.round(estimate))} 分钟`; - const min = Number(value.min ?? value.min_minutes ?? value.lower ?? value.lower_minutes); - const max = Number(value.max ?? value.max_minutes ?? value.upper ?? value.upper_minutes); + const min = Number(value.min ?? value.minMinutes ?? value.lower ?? value.lowerMinutes); + const max = Number(value.max ?? value.maxMinutes ?? value.upper ?? value.upperMinutes); if (Number.isFinite(min) && Number.isFinite(max)) { return `预计 ${Math.max(0, Math.round(min))}-${Math.max(0, Math.round(max))} 分钟`; } @@ -78,23 +80,14 @@ export const visitorStatusMeta = (status, peopleAhead) => { }; } -export const normalizePublicStatus = (ticket = {}) => { - const partySize = Number(ticket.party_size); - return { - ...ticket, - party_size: Number.isFinite(partySize) && partySize > 0 ? partySize : 1, - ticket_number: ticket.ticket_number || ticket.display_number || "暂无", - }; -} - export const buildTicketKey = (ticket = {}) => { - return `${ticket.project?.id ?? ticket.project_name}:${ticket.ticket_number}`; + return `${ticket.project?.id ?? ticket.projectName}:${ticket.ticketNumber}`; } export const resolveStatusTarget = (response = {}) => { - if (response.public_token) return { token: response.public_token }; + if (response.publicToken) return { token: response.publicToken }; - const url = response.public_url || response.status_path || ""; + const url = response.publicUrl || response.statusPath || ""; const match = String(url).match(/\/visitor\/([^/?#]+)/); if (match?.[1]) return { token: decodeURIComponent(match[1]) }; @@ -105,7 +98,7 @@ export const extractVisitorToken = (value) => { const text = String(value ?? "").trim(); if (!text) return ""; - const tokenMatch = text.match(/(?:^|[?&#])(?:token|public_token)=([^&#]+)/); + const tokenMatch = text.match(/(?:^|[?&#])(?:token|publicToken)=([^&#]+)/); if (tokenMatch?.[1]) return decodeMaybe(tokenMatch[1]); const visitorMatch = text.match(/\/visitor\/([^/?#]+)/);