/** 预约状态:后端中文 ↔ 管理端/门店 PC 日历 UI */ export function pickRowField(row, ...keys) { if (!row) return undefined for (let i = 0; i < keys.length; i++) { const v = row[keys[i]] if (v !== undefined && v !== null && v !== '') return v } return undefined } export function parseApiDateTime(val) { if (val == null || val === '') return null if (val instanceof Date) return Number.isNaN(val.getTime()) ? null : val if (typeof val === 'number') { const d = new Date(val) return Number.isNaN(d.getTime()) ? null : d } const str = String(val).trim() if (!str) return null if (/^\d+$/.test(str)) { const d = new Date(Number(str)) return Number.isNaN(d.getTime()) ? null : d } const normalized = str.replace('T', ' ').replace(/-/g, '/') const d = new Date(normalized) if (!Number.isNaN(d.getTime())) return d const d2 = new Date(str) return Number.isNaN(d2.getTime()) ? null : d2 } /** 后端 LqYyjl 列表 yysj 范围:毫秒时间戳,毫秒时间戳 */ export function buildYysjTimestampRange(startDate, endDate) { const s = parseApiDateTime(startDate) const e = parseApiDateTime(endDate) if (!s || !e) return '' return [s.getTime(), e.getTime()].join(',') } export function apiStatusToUi(fStatus) { const s = (fStatus || '').trim() if (s === '已预约' || s === '已确认') return 'booked' if (s === '服务中') return 'serving' if (s === '已转耗卡' || s === '已完成') return 'converted' if (s === '已取消') return 'cancelled' return 'booked' } export function uiStatusToApi(uiStatus) { if (uiStatus === 'booked') return '已预约' if (uiStatus === 'serving') return '服务中' if (uiStatus === 'converted') return '已完成' if (uiStatus === 'cancelled') return '已取消' return '' } export function statusText(uiOrApi) { const ui = apiStatusToUi(uiOrApi) === 'booked' && !['booked', 'serving', 'converted', 'cancelled'].includes(uiOrApi) ? uiOrApi : apiStatusToUi(uiOrApi) if (ui === 'booked' || uiOrApi === '已预约') return '已预约' if (ui === 'serving' || uiOrApi === '服务中') return '服务中' if (ui === 'converted' || uiOrApi === '已完成' || uiOrApi === '已转耗卡') return '已完成' if (ui === 'cancelled' || uiOrApi === '已取消') return '已取消' return uiOrApi || '无' } /** 是否应展示关联耗卡(已转耗卡/已完成或存在有效耗卡) */ export function hasLinkedConsume(row) { if (!row) return false if (pickRowField(row, 'consumeId', 'ConsumeId')) return true if (pickRowField(row, 'hasEffectiveConsume', 'HasEffectiveConsume')) return true const fStatus = pickRowField(row, 'F_Status', 'fStatus', 'f_Status', 'status') return apiStatusToUi(fStatus) === 'converted' } export function mapRelatedConsumeList(list) { return (list || []).map(item => ({ id: pickRowField(item, 'id', 'Id') || '', hksj: pickRowField(item, 'hksj', 'Hksj'), xfje: pickRowField(item, 'xfje', 'Xfje'), hymc: pickRowField(item, 'hymc', 'Hymc') || '无' })).filter(item => item.id) } export function formatDateYMD(d) { const dt = d instanceof Date ? d : parseApiDateTime(d) if (!dt) return '' const y = dt.getFullYear() const m = String(dt.getMonth() + 1).padStart(2, '0') const day = String(dt.getDate()).padStart(2, '0') return `${y}-${m}-${day}` } export function formatTimeHM(dt) { if (!dt) return '' const d = dt instanceof Date ? dt : parseApiDateTime(dt) if (!d) { const str = String(dt) if (str.includes(' ')) { const part = str.split(' ')[1] || '' return part.substring(0, 5) } return str.length >= 5 ? str.substring(0, 5) : '' } return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}` } export function mapYyjlListRow(row) { const yysj = pickRowField(row, 'yysj', 'Yysj') const yyjs = pickRowField(row, 'yyjs', 'Yyjs') const fStatus = pickRowField(row, 'F_Status', 'f_Status', 'fStatus') const date = formatDateYMD(yysj) const startTime = formatTimeHM(yysj) const endTime = formatTimeHM(yyjs) const status = apiStatusToUi(fStatus) return { id: pickRowField(row, 'id', 'Id', 'F_Id'), memberId: pickRowField(row, 'gk', 'Gk'), memberName: pickRowField(row, 'gkxm', 'Gkxm') || '无', date, startTime, endTime, timeRange: startTime && endTime ? `${startTime}-${endTime}` : '', roomId: pickRowField(row, 'roomId', 'RoomId', 'F_RoomId') || '', roomName: pickRowField(row, 'roomName', 'RoomName', 'F_RoomName') || '', therapistIds: pickRowField(row, 'yyjks', 'Yyjks') ? [pickRowField(row, 'yyjks', 'Yyjks')] : [], therapistNames: pickRowField(row, 'yyjksName', 'YyjksName') ? [pickRowField(row, 'yyjksName', 'YyjksName')] : [], staffName: pickRowField(row, 'yyjksName', 'YyjksName') || '无', yyr: pickRowField(row, 'yyr', 'Yyr') || '', yyrName: pickRowField(row, 'yyrName', 'YyrName') || '无', storeId: pickRowField(row, 'djmd', 'Djmd') || '', storeName: pickRowField(row, 'djmdName', 'DjmdName') || '', project: pickRowField(row, 'yytyxm', 'Yytyxm') || pickRowField(row, 'gkxm', 'Gkxm') || '无', status, fStatus: fStatus, remark: pickRowField(row, 'remark', 'Remark', 'F_Remark') || '', InviteId: pickRowField(row, 'InviteId', 'inviteId') || '', projectSummary: pickRowField(row, 'projectSummary', 'ProjectSummary') || '', consumeId: pickRowField(row, 'consumeId', 'ConsumeId') || '', hasEffectiveConsume: !!pickRowField(row, 'hasEffectiveConsume', 'HasEffectiveConsume'), mobile: '', items: [], itemLabels: row.yytyxm ? [row.yytyxm] : [], colorKey: 'blue' } } export function mapYyjlInfoToFormRecord(info, pxList = []) { const date = formatDateYMD(info.yysj) const items = (pxList || []).map(px => ({ projectId: px.billingItemId || px.px, itemId: px.px, billingItemId: px.billingItemId, label: px.pxmc, count: Number(px.projectNumber) || 1, workers: (px.jksList || []).map(j => ({ workerId: j.jks, workerName: j.jksxm })) })) const itemLabels = items.map(x => `${x.label}×${x.count}`) const therapistIds = [] const therapistNames = [] items.forEach(it => { ;(it.workers || []).forEach(w => { if (w.workerId && !therapistIds.includes(w.workerId)) { therapistIds.push(w.workerId) therapistNames.push(w.workerName || w.workerId) } }) }) if (!therapistIds.length && info.yyjks) { therapistIds.push(info.yyjks) therapistNames.push(info.yyjksName || info.yyjks) } return { id: info.id, memberId: info.gk, memberName: info.gkxm || '无', storeId: info.djmd, storeName: info.djmdName || '无', yyr: info.yyr, yyrName: info.yyrName || '无', date, startTime: formatTimeHM(info.yysj), endTime: formatTimeHM(info.yyjs), roomId: info.roomId, roomName: info.roomName || '无', remark: info.remark || '', status: apiStatusToUi(info.F_Status), fStatus: info.F_Status, InviteId: info.InviteId || '', consumeId: info.consumeId || '', hasEffectiveConsume: !!info.hasEffectiveConsume, relatedConsumes: mapRelatedConsumeList(info.relatedConsumeList), items, itemLabels, therapistIds, therapistNames } }