import { PolicyProtocolRequest } from "../../../components/appointmentBooking/deposit/components/PolicyModal/types.js"; import { SkuValue } from "../../../plus/skuOptionsSelection/types.js"; import { WeighingValue } from "../components/weighing/types.js"; import { flowModuleRegistry } from "./registry.js"; import { ExtendData, ProductData } from "../types.js"; import { Dayjs } from "dayjs"; import React from "react"; import { CompiledSelectionFlowModuleConfig, FlowModuleHandle, SelectionFlowContextValue, SelectionFlowModuleAdapter, SelectionFlowModuleConfig, SelectionFlowModuleInput, SelectionFlowStepConfig } from "@pisell/materials"; //#region src/pro/skuDetailModal/flow/types.d.ts /** 注册表是模块类型的唯一事实来源。 */ type SelectionModuleType = Extract; type SelectionFlowModuleConfig$1 = SelectionFlowModuleConfig; type SelectionFlowModuleInput$1 = SelectionFlowModuleInput; type SelectionFlowStepConfig$1 = SelectionFlowStepConfig; /** 商品级步骤解析读取完整水合后的商品及本次打开模式。 */ interface ResolveSelectionFlowStepsContext { /** 已完成详情水合的当前商品;Resolver 不应修改此对象。 */ readonly productData: Readonly; /** 创建与编辑共用同一 Flow 引擎,但业务步骤可以按操作阶段配置。 */ readonly selectionFlowMode?: 'create' | 'edit'; } /** * 返回数组表示本次启用 Selection Flow;返回 null 表示明确沿用旧流程。 * 空数组会作为配置错误交给流程编译器阻断。 */ type ResolveSelectionFlowSteps = (context: ResolveSelectionFlowStepsContext) => readonly SelectionFlowStepConfig$1[] | null; type CompiledSelectionFlowModuleConfig$1 = CompiledSelectionFlowModuleConfig; interface SelectionFlowBookingDraft { /** 外部 BookingContext 客户快照,只用于 Holder 失效判断,不进入最终输出。 */ customerId?: string | number; /** 预约开始日期;尚未选择时为 null。 */ startDate: Dayjs | null; /** 预约结束日期;单日预约通常与开始日期一致。 */ endDate: Dayjs | null; /** 预约开始时刻,使用现有预约链路的时间字符串。 */ startTime: string | null; /** 预约结束时刻,使用现有预约链路的时间字符串。 */ endTime: string | null; /** 现有预约链路除数字分钟外,也会使用 flexible_* 等字符串值。 */ duration: number | string | null; /** 按资源类型归类的已选资源。 */ resource: ExtendData['resource'] | null; /** 已选资源对应的容量信息。 */ capacity: ExtendData['capacity'] | null; /** 已选择 holder 的 ID 列表;仅保留选择态,具体加车语义由旧链路处理。 */ holderIds: Array; /** target: time 返回的完整候选;不得由日期/时间字符串重新拼装。 */ selectedCandidate?: SelectionFlowAvailabilityCandidate; /** target: resource 返回、最终可交给 Host commit 的完整 range。 */ commitCandidate?: SelectionFlowAvailabilityCandidate; /** 资源模块按 UBS 资源需求组生成的选择结果。 */ requirementSelections?: Array<{ requirementGroupId?: string; formId?: number | string; resourceIds: Array; }>; } /** 流程内部草稿,不包含购物车或订单转换结果。 */ interface SelectionFlowDraft { /** 当前完整商品详情;日期等模块水合后会更新此字段。 */ productData: ProductData; /** SKU 模块已选规格;无规格商品可为空。 */ skuValue?: SkuValue; /** 本次购买数量。 */ quantity: number; /** 订单行备注。 */ note?: string; /** 商品折扣快照。 */ discounts?: ProductData['discount_list']; /** 手动改价后的复合单位价;null 表示未改价。 */ priceOverride?: number | null; /** 称重商品的称重结果。 */ weighing?: WeighingValue | null; /** 预约相关选择态。 */ booking: SelectionFlowBookingDraft; /** 当前完整预约时段对应的权威报价;未启用报价模块的旧 Flow 可为空。 */ bookingPrice?: SelectionFlowBookingPriceState; } interface SelectionFlowBookingPriceQuote { /** 主商品或已选 variant 的单位报价,不含 option / bundle。 */ sourceUnitPrice: number; /** 已同步权威价格的 SKU 选择快照。 */ pricedSkuValue?: SkuValue; /** option 每一件商品合计的附加金额。 */ optionUnitTotal: number; /** bundle 每一件商品合计的附加金额。 */ bundleUnitTotal: number; /** 主商品、option、bundle 合并后的商品单位价。 */ compositeUnitPrice: number; /** compositeUnitPrice 乘购买数量后的行金额,不含折扣。 */ compositeLineTotal: number; quotationShelfId?: string | number; segments?: unknown[]; } interface SelectionFlowBookingPriceRequest { productData: ProductData; skuValue?: SkuValue; quantity: number; selectedCandidate: SelectionFlowAvailabilityCandidate; /** 用户最终选中的预约总时长。 */ duration: number | string | null; /** 后端 quotation 分段使用的商品计价粒度;与用户总时长语义不同。 */ pricingDuration?: { type: string; value: number | string; }; customerId?: string | number; /** 宿主提供的 channel / business 等定价上下文指纹。 */ contextKey: string; } interface SelectionFlowBookingPriceSnapshot { requestKey: string; quote: SelectionFlowBookingPriceQuote; } interface SelectionFlowBookingPriceState { status: 'idle' | 'loading' | 'ready' | 'error'; /** quote 所属的稳定请求指纹;ready 时必须与当前选择重新计算出的 key 一致。 */ requestKey?: string; quote?: SelectionFlowBookingPriceQuote; message?: string; } interface SelectionFlowBookingPriceService { /** channel / business / strategy context 的稳定指纹。 */ contextKey: string; /** * 明确的报价拆段粒度。它描述计价规则,不是商品的预约时长;缺省时仅按 * candidate 的开始时刻报价一次,禁止从 productData.duration 自动推断。 */ pricingDuration?: SelectionFlowBookingPriceRequest['pricingDuration']; calculate: (request: SelectionFlowBookingPriceRequest) => Promise; /** 编辑场景的原订单行价格;命中 requestKey 时不发远程报价。 */ initialSnapshot?: SelectionFlowBookingPriceSnapshot; } type SelectionDraftField = 'productData' | 'skuValue' | 'quantity' | 'note' | 'discounts' | 'priceOverride' | 'weighing' | 'bookingPrice' | 'booking.customerId' | 'booking.startDate' | 'booking.endDate' | 'booking.startTime' | 'booking.endTime' | 'booking.duration' | 'booking.resource' | 'booking.capacity' | 'booking.holderIds' | 'booking.selectedCandidate' | 'booking.commitCandidate' | 'booking.requirementSelections'; type SelectionFlowDraftPatch = Partial> & { booking?: Partial; }; type SelectionFlowDraftUpdate = SelectionFlowDraftPatch | ((draft: Readonly) => SelectionFlowDraftPatch); type SelectionFlowAvailabilityStatus = 'available' | 'limited' | 'full' | 'conflict' | 'unavailable' | 'past'; /** UnifiedBookingSales 返回的上下文化时间候选;Flow 必须原样保存。 */ interface SelectionFlowAvailabilityCandidate { key: string; productId: number | string; source: 'session_schedule' | 'duration_window'; startAt: string; endAt: string; date: string; startTime: string; endTime: string; timezone: string; scheduleRefs: Array>; primaryScheduleRef?: Record; availabilityContextId: string; availabilityContextVersion: number; status?: SelectionFlowAvailabilityStatus; maxPartySize?: number; reasonCodes?: string[]; requirementGroups?: unknown[]; } interface SelectionFlowAvailabilityService { /** Host 打开预约商品时由 UnifiedBookingSales prepare 得到。 */ contextId: string; productId: number | string; partySize: number; getProductTimeRanges: (contextId: string, request: Record) => Promise>; queryBookingAvailability: (contextId: string, request: Record) => Promise<{ target: 'product' | 'date' | 'time' | 'resource'; data: unknown[]; [key: string]: unknown; }>; } /** 由 SkuDetailModal 上层注入的现有服务;Flow 只透传。 */ interface SelectionFlowServices { /** 当前 Host 打开 Flow 的业务模式;编辑专属 module 通过它决定是否参与。 */ selectionFlowMode?: 'create' | 'edit'; /** POS 旧 Edit service 是否应在资源步骤展示可编辑 Price。 */ showServicePrice?: boolean; /** * 预约编辑由外层延后提交时,Resource 模块需同时保留旧 BookingContext * 可识别的 resource map,避免 availability 选择在后续 legacy 转换中丢失。 */ deferAvailabilityCommit?: boolean; /** 直接复用 Host 当前 UnifiedBookingSales availability context。 */ availability?: SelectionFlowAvailabilityService; /** 复用宿主已有资源组件实例;Resource 模块不创建平行资源请求。 */ resourcesRef?: React.RefObject | React.MutableRefObject; /** 获取宿主当前 cacheItem,供 time/resource 模块兼容既有数据结构。 */ getCurrentCacheItem?: () => unknown; /** 日期变化后复用宿主现有商品详情请求,不在 Flow 内重新定义接口。 */ hydrateProductDataForDate?: (date: Dayjs, currentProductData: ProductData) => Promise; /** * 时间片每行列数,由宿主根据真实平台决定。 * Flow 模块不通过屏幕宽度猜测设备,避免 Kiosk 的 phone Drawer 被当成手机。 */ timeSlotColumns?: number; /** POS 柜台是否允许选择容量已满(remaining=0)的预约日期/时间片。 */ allowUnavailableBookingSelection?: boolean; /** POS 柜台是否允许使用 resourceId=0 表示不指定资源。 */ allowNoResourceBookingSelection?: boolean; /** 商品详情展示平台,由宿主显式传入,避免 Flow 通过屏幕宽度猜测 POS/Kiosk。 */ presentationPlatform?: 'default' | 'kiosk' | 'pos'; /** 复用当前 Pisell OS 销售实例读取资源协议,不在 Flow 内创建新的 OS 实例。 */ getProtocol?: PolicyProtocolRequest; /** 按完整预约候选向宿主请求权威智能报价。 */ bookingPrice?: SelectionFlowBookingPriceService; [key: string]: unknown; } type SelectionFlowContextValue$1 = SelectionFlowContextValue; interface FlowModuleProps { /** 当前流程上下文。 */ context: SelectionFlowContextValue$1; /** 已编译的模块配置,包含稳定 key 与所在步骤索引。 */ module: CompiledSelectionFlowModuleConfig$1; /** 注册模块校验句柄;卸载时应以 null 注销。 */ registerHandle?: (handle: FlowModuleHandle | null) => void; } /** * string 泛型让 registry 可以先声明适配器,再由其 key 推导 * SelectionModuleType,避免运行时注册和类型循环。 */ type SelectionFlowModuleAdapter$1 = SelectionFlowModuleAdapter & { /** 组件和错误展示策略属于 SKU Detail 业务 UI,不进入原子 Flow 契约。 */Component: React.ComponentType>; rendersOwnError?: boolean; }; //#endregion export { CompiledSelectionFlowModuleConfig$1 as CompiledSelectionFlowModuleConfig, FlowModuleProps, ResolveSelectionFlowSteps, ResolveSelectionFlowStepsContext, SelectionDraftField, SelectionFlowBookingPriceQuote, SelectionFlowBookingPriceRequest, SelectionFlowBookingPriceService, SelectionFlowBookingPriceSnapshot, SelectionFlowBookingPriceState, SelectionFlowContextValue$1 as SelectionFlowContextValue, SelectionFlowDraft, SelectionFlowDraftUpdate, SelectionFlowModuleAdapter$1 as SelectionFlowModuleAdapter, SelectionFlowModuleConfig$1 as SelectionFlowModuleConfig, SelectionFlowModuleInput$1 as SelectionFlowModuleInput, SelectionFlowServices, SelectionFlowStepConfig$1 as SelectionFlowStepConfig, SelectionModuleType };