酒店
This commit is contained in:
1 parent
2a1d0b1d11
commit
14f4b160e6
7 files changed
+376
-47
No files matched your search
@@ -588,7 +588,7 @@ const submitSingle = async () => {
|
||||
saving.value = true
|
||||
try {
|
||||
const payload: any = {
|
||||
id: single.id || undefined,
|
||||
id: single.id != null && single.id !== "" ? String(single.id) : undefined,
|
||||
stayDate: single.stayDate,
|
||||
productCode: single.productCode,
|
||||
sellPrice: single.sellPrice !== "" ? Number(single.sellPrice) : undefined,
|
||||
|
||||
@@ -604,7 +604,7 @@ const submitEdit = async () => {
|
||||
const payload: any = {
|
||||
...(hotel.value || {}),
|
||||
...buildPayload(),
|
||||
id: hotel.value?.id || hotelId.value,
|
||||
id: String(hotel.value?.id || hotelId.value),
|
||||
}
|
||||
const res: any = await smHotelAdminApi.update(payload)
|
||||
if (res?.code !== 0) {
|
||||
|
||||
@@ -354,6 +354,7 @@ const batchSetStatus = async (status: "ACTIVE" | "OFFLINE") => {
|
||||
}
|
||||
const res: any = await smHotelAdminApi.update({
|
||||
...hotel,
|
||||
id: hotel?.id != null ? String(hotel.id) : id,
|
||||
upstreamStatus: status,
|
||||
})
|
||||
if (res?.code === 0) ok++
|
||||
|
||||
@@ -30,7 +30,12 @@
|
||||
</view>
|
||||
<view v-else-if="!list.length" class="ord__empty">暂无订单</view>
|
||||
<view v-else class="ord__list">
|
||||
<view v-for="row in list" :key="row.id" class="card" :class="{ 'card--warn': isPriceAnomaly(row) || row.orderStatus === 'PENDING_CANCEL' }">
|
||||
<view
|
||||
v-for="row in list"
|
||||
:key="row.id"
|
||||
class="card"
|
||||
:class="{ 'card--warn': isPriceAnomaly(row) || row.orderStatus === 'PENDING_CANCEL' }"
|
||||
@tap="openOrderDetail(row)">
|
||||
<view class="card__head">
|
||||
<text class="card__hotel">{{ row.hotelName || row.hotelCode || "酒店" }}</text>
|
||||
<text class="card__status" :class="statusClass(row.orderStatus)">
|
||||
@@ -39,34 +44,54 @@
|
||||
</view>
|
||||
<view v-if="isPriceAnomaly(row)" class="card__badge">结算价异常 · 请人工判断</view>
|
||||
<view v-else-if="row.orderStatus === 'PENDING_CANCEL'" class="card__badge">下游申请取消 · 请确认</view>
|
||||
<view class="card__kv">
|
||||
<text class="k">单号</text>
|
||||
<text class="v mono copyable" @tap.stop="copyText(orderNo(row))">
|
||||
{{ orderNo(row) }}
|
||||
<text v-if="orderNo(row) !== '-'" class="copy-tip">复制</text>
|
||||
</text>
|
||||
</view>
|
||||
<view class="card__kv">
|
||||
<text class="k">房型/产品</text>
|
||||
<text class="v">{{ row.productName || row.roomTypeName || row.productCode || "-" }}</text>
|
||||
</view>
|
||||
<view class="card__kv">
|
||||
<text class="k">入住</text>
|
||||
<text class="v">{{ row.checkInDate || "-" }} ~ {{ row.checkOutDate || "-" }} · {{ row.count || 1 }}间</text>
|
||||
</view>
|
||||
<view class="card__kv">
|
||||
<text class="k">联系人</text>
|
||||
<text class="v">{{ row.contactName || "-" }} {{ row.contactPhone || "" }}</text>
|
||||
</view>
|
||||
<view class="card__kv">
|
||||
<text class="k">支付时间</text>
|
||||
<text class="v">{{ fmtTime(row.createTime) }}</text>
|
||||
</view>
|
||||
<view v-if="row.orderStatus === 'PENDING_CANCEL' && row.cancelRuleHint" class="card__line tip">
|
||||
取消规则:{{ row.cancelRuleHint }}
|
||||
</view>
|
||||
<view class="card__line">{{ row.productName || row.roomTypeName || row.productCode || "-" }}</view>
|
||||
<view class="card__line muted">
|
||||
{{ row.checkInDate || "-" }} ~ {{ row.checkOutDate || "-" }} · {{ row.count || 1 }}间
|
||||
</view>
|
||||
<view class="card__line muted">
|
||||
{{ row.contactName || "-" }} {{ row.contactPhone || "" }}
|
||||
</view>
|
||||
<view v-if="row.failMsg" class="card__line tip">{{ row.failMsg }}</view>
|
||||
<view class="card__foot">
|
||||
<view class="card__foot" @tap.stop>
|
||||
<text class="card__amt">¥{{ formatAmt(row.sellAmount) }}</text>
|
||||
<view v-if="canAct(row)" class="card__acts">
|
||||
<button class="btn btn--ghost" :disabled="actingId === row.id" @tap="onReject(row)">
|
||||
拒绝
|
||||
</button>
|
||||
<button class="btn btn--primary" :disabled="actingId === row.id" @tap="onConfirm(row)">
|
||||
确认接单
|
||||
</button>
|
||||
</view>
|
||||
<view v-else-if="canActCancel(row)" class="card__acts">
|
||||
<button class="btn btn--ghost" :disabled="actingId === row.id" @tap="onRejectCancel(row)">
|
||||
拒绝取消
|
||||
</button>
|
||||
<button class="btn btn--danger" :disabled="actingId === row.id" @tap="onConfirmCancel(row)">
|
||||
同意取消
|
||||
</button>
|
||||
<view class="card__acts">
|
||||
<button class="btn btn--link" @tap.stop="goHotel(row)">酒店</button>
|
||||
<button class="btn btn--link" @tap.stop="openOrderDetail(row)">详情</button>
|
||||
<template v-if="canAct(row)">
|
||||
<button class="btn btn--ghost" :disabled="actingId === row.id" @tap.stop="onReject(row)">
|
||||
拒绝
|
||||
</button>
|
||||
<button class="btn btn--primary" :disabled="actingId === row.id" @tap.stop="onConfirm(row)">
|
||||
确认接单
|
||||
</button>
|
||||
</template>
|
||||
<template v-else-if="canActCancel(row)">
|
||||
<button class="btn btn--ghost" :disabled="actingId === row.id" @tap.stop="onRejectCancel(row)">
|
||||
拒绝取消
|
||||
</button>
|
||||
<button class="btn btn--danger" :disabled="actingId === row.id" @tap.stop="onConfirmCancel(row)">
|
||||
同意取消
|
||||
</button>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -78,20 +103,88 @@
|
||||
<view v-else-if="list.length" class="ord__more muted">没有更多了</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单详情 -->
|
||||
<view v-if="detailShow" class="sheet-mask" @tap="closeDetail">
|
||||
<view class="sheet" @tap.stop>
|
||||
<view class="sheet__hd">
|
||||
<text class="sheet__title">订单详情</text>
|
||||
<text class="sheet__close" @tap="closeDetail">关闭</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="sheet__bd" v-if="detailLoading">
|
||||
<view class="ord__loading"><view class="ord__spin" /><text>加载中…</text></view>
|
||||
</scroll-view>
|
||||
<scroll-view v-else scroll-y class="sheet__bd">
|
||||
<template v-if="detail?.order">
|
||||
<view class="dsec">
|
||||
<view class="dsec__t">订单</view>
|
||||
<view class="drow"><text class="dk">状态</text><text class="dv">{{ statusText(detail.order.orderStatus) }}</text></view>
|
||||
<view class="drow">
|
||||
<text class="dk">渠道单号</text>
|
||||
<text class="dv mono copyable" @tap="copyText(detail.order.channelNo)">
|
||||
{{ detail.order.channelNo || "-" }}
|
||||
<text v-if="detail.order.channelNo" class="copy-tip">复制</text>
|
||||
</text>
|
||||
</view>
|
||||
<view class="drow">
|
||||
<text class="dk">平台单号</text>
|
||||
<text class="dv mono copyable" @tap="copyText(detail.order.platformNo)">
|
||||
{{ detail.order.platformNo || "-" }}
|
||||
<text v-if="detail.order.platformNo" class="copy-tip">复制</text>
|
||||
</text>
|
||||
</view>
|
||||
<view class="drow"><text class="dk">入住离店</text><text class="dv">{{ detail.order.checkInDate }} ~ {{ detail.order.checkOutDate }}</text></view>
|
||||
<view class="drow"><text class="dk">间数</text><text class="dv">{{ detail.order.count || 1 }}</text></view>
|
||||
<view class="drow"><text class="dk">应收</text><text class="dv strong">¥{{ formatAmt(detail.order.sellAmount) }}</text></view>
|
||||
<view class="drow"><text class="dk">成本</text><text class="dv">¥{{ formatAmt(detail.order.costAmount) }}</text></view>
|
||||
<view class="drow"><text class="dk">支付时间</text><text class="dv">{{ fmtTime(detail.order.createTime) }}</text></view>
|
||||
<view class="drow"><text class="dk">联系人</text><text class="dv">{{ detail.order.contactName || "-" }} {{ detail.order.contactPhone || "" }}</text></view>
|
||||
<view v-if="detail.order.cancelRuleHint" class="drow"><text class="dk">取消规则</text><text class="dv tip">{{ detail.order.cancelRuleHint }}</text></view>
|
||||
<view v-if="detail.order.failMsg" class="drow"><text class="dk">失败说明</text><text class="dv tip">{{ detail.order.failMsg }}</text></view>
|
||||
</view>
|
||||
<view v-if="detail.hotel" class="dsec">
|
||||
<view class="dsec__t">酒店</view>
|
||||
<view class="drow"><text class="dk">名称</text><text class="dv">{{ detail.hotel.hotelName || "-" }}</text></view>
|
||||
<view class="drow"><text class="dk">编码</text><text class="dv mono">{{ detail.hotel.hotelCode || "-" }}</text></view>
|
||||
<view class="drow"><text class="dk">地址</text><text class="dv">{{ detail.hotel.address || detail.hotel.district || "-" }}</text></view>
|
||||
<view class="drow"><text class="dk">电话</text><text class="dv">{{ detail.hotel.phone || "-" }}</text></view>
|
||||
<view class="drow"><text class="dk">来源</text><text class="dv">{{ detail.hotel.dataSource || "-" }}</text></view>
|
||||
<button class="btn btn--primary sheet__btn" @tap="goHotelFromDetail">查看酒店详情</button>
|
||||
</view>
|
||||
<view v-if="detail.product" class="dsec">
|
||||
<view class="dsec__t">产品</view>
|
||||
<view class="drow"><text class="dk">名称</text><text class="dv">{{ detail.product.productName || detail.roomTypeName || "-" }}</text></view>
|
||||
<view class="drow"><text class="dk">编码</text><text class="dv mono">{{ detail.product.productCode || "-" }}</text></view>
|
||||
<view class="drow"><text class="dk">房型</text><text class="dv">{{ detail.product.roomTypeCode || detail.order.roomTypeCode || "-" }}</text></view>
|
||||
</view>
|
||||
<view v-if="guestLines.length" class="dsec">
|
||||
<view class="dsec__t">入住人</view>
|
||||
<view v-for="(g, i) in guestLines" :key="i" class="drow">
|
||||
<text class="dk">{{ i + 1 }}</text>
|
||||
<text class="dv">{{ g }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view v-else class="ord__empty">暂无详情</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="tab-spacer" />
|
||||
<hm-tabbar active="orders" />
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
import { computed, ref } from "vue"
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app"
|
||||
import { smHotelAdminApi } from "@@/api"
|
||||
import { router } from "@@/index"
|
||||
import { canAccessHotelManage, refreshUserPermissions } from "@@/utils/user-role"
|
||||
import HmTabbar from "./components/hm-tabbar.vue"
|
||||
import HmNavBar from "./components/hm-nav-bar.vue"
|
||||
import { consumeHmBridgeAuth, markHmFromAppWebView, goHotelManageMine, notifyHotelManageH5Ready } from "./hm-h5-config"
|
||||
import { useHmEmbed } from "./use-hm-embed"
|
||||
import { setHmHotel } from "./hm-context"
|
||||
|
||||
const { embedInApp } = useHmEmbed()
|
||||
const loading = ref(false)
|
||||
@@ -100,6 +193,12 @@ const list = ref<any[]>([])
|
||||
const page = ref(1)
|
||||
const hasMore = ref(true)
|
||||
const filter = ref<"pending" | "cancel" | "all">("pending")
|
||||
/** 防止 onShow / 切 Tab 并发请求互相覆盖导致「全部」空白 */
|
||||
let fetchSeq = 0
|
||||
|
||||
const detailShow = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const detail = ref<any>(null)
|
||||
|
||||
const tabs = [
|
||||
{ key: "pending" as const, label: "待确认" },
|
||||
@@ -142,12 +241,33 @@ const statusClass = (s?: string) => {
|
||||
return ""
|
||||
}
|
||||
|
||||
const orderNo = (row: any) => String(row?.channelNo || row?.platformNo || row?.id || "-")
|
||||
|
||||
const copyText = (text?: string | number | null) => {
|
||||
const v = String(text || "").trim()
|
||||
if (!v || v === "-") {
|
||||
uni.showToast({ title: "无可复制内容", icon: "none" })
|
||||
return
|
||||
}
|
||||
uni.setClipboardData({
|
||||
data: v,
|
||||
success: () => uni.showToast({ title: "已复制", icon: "none" }),
|
||||
fail: () => uni.showToast({ title: "复制失败", icon: "none" }),
|
||||
})
|
||||
}
|
||||
|
||||
const formatAmt = (v: any) => {
|
||||
const n = Number(v)
|
||||
if (Number.isNaN(n)) return "0"
|
||||
if (Number.isNaN(n)) return "0.00"
|
||||
return n.toFixed(2)
|
||||
}
|
||||
|
||||
const fmtTime = (v: any) => {
|
||||
if (!v) return "-"
|
||||
const s = String(v).replace("T", " ")
|
||||
return s.length > 16 ? s.slice(0, 16) : s
|
||||
}
|
||||
|
||||
const isLocalRow = (row: any) => {
|
||||
const ds = String(row?.hotelDataSource || "")
|
||||
.trim()
|
||||
@@ -170,6 +290,21 @@ const canActCancel = (row: any) => {
|
||||
return !!row && row.orderStatus === "PENDING_CANCEL"
|
||||
}
|
||||
|
||||
const guestLines = computed(() => {
|
||||
const g = detail.value?.guests
|
||||
if (!g) return [] as string[]
|
||||
const arr = Array.isArray(g) ? g : [g]
|
||||
return arr
|
||||
.map((item: any) => {
|
||||
if (typeof item === "string") return item
|
||||
const name = item?.name || item?.guestName || item?.contactName || ""
|
||||
const phone = item?.phone || item?.mobile || item?.contactPhone || ""
|
||||
const idNo = item?.idNo || item?.idCard || ""
|
||||
return [name, phone, idNo].filter(Boolean).join(" · ") || JSON.stringify(item)
|
||||
})
|
||||
.filter(Boolean)
|
||||
})
|
||||
|
||||
const buildParams = () => {
|
||||
const params: Record<string, any> = {
|
||||
current: page.value,
|
||||
@@ -181,22 +316,23 @@ const buildParams = () => {
|
||||
params.hotelDataSource = "local"
|
||||
} else if (filter.value === "cancel") {
|
||||
params.orderStatus = "PENDING_CANCEL"
|
||||
// 待取消含本地与上游酒店,人工核对取消规则
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
const fetchPage = async (reset: boolean) => {
|
||||
if (loading.value) return
|
||||
const seq = ++fetchSeq
|
||||
if (reset) {
|
||||
page.value = 1
|
||||
hasMore.value = true
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
if (reset) {
|
||||
page.value = 1
|
||||
hasMore.value = true
|
||||
}
|
||||
const res: any = await smHotelAdminApi.orderPage(buildParams())
|
||||
if (seq !== fetchSeq) return
|
||||
if (res?.code !== 0) {
|
||||
uni.showToast({ title: res?.msg || "加载失败", icon: "none" })
|
||||
if (reset) list.value = []
|
||||
return
|
||||
}
|
||||
const records = res?.data?.records || res?.data?.list || []
|
||||
@@ -204,16 +340,21 @@ const fetchPage = async (reset: boolean) => {
|
||||
list.value = reset ? records : list.value.concat(records)
|
||||
hasMore.value = list.value.length < total
|
||||
} catch (e: any) {
|
||||
if (seq !== fetchSeq) return
|
||||
uni.showToast({ title: e?.msg || "加载失败", icon: "none" })
|
||||
if (reset) list.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
notifyHotelManageH5Ready()
|
||||
if (seq === fetchSeq) {
|
||||
loading.value = false
|
||||
notifyHotelManageH5Ready()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const switchFilter = (key: "pending" | "cancel" | "all") => {
|
||||
if (filter.value === key) return
|
||||
filter.value = key
|
||||
list.value = []
|
||||
fetchPage(true)
|
||||
}
|
||||
|
||||
@@ -223,6 +364,54 @@ const loadMore = () => {
|
||||
fetchPage(false)
|
||||
}
|
||||
|
||||
const goHotel = (row: any) => {
|
||||
const id = row?.hotelId
|
||||
if (!id) {
|
||||
uni.showToast({ title: "无关联酒店", icon: "none" })
|
||||
return
|
||||
}
|
||||
setHmHotel({ id, hotelName: row.hotelName, hotelCode: row.hotelCode })
|
||||
router.jump("/pages/hotel-manage/detail", { id: String(id) })
|
||||
}
|
||||
|
||||
const goHotelFromDetail = () => {
|
||||
const h = detail.value?.hotel
|
||||
const id = h?.id || detail.value?.order?.hotelId
|
||||
if (!id) {
|
||||
uni.showToast({ title: "无关联酒店", icon: "none" })
|
||||
return
|
||||
}
|
||||
detailShow.value = false
|
||||
setHmHotel({ id, hotelName: h?.hotelName, hotelCode: h?.hotelCode })
|
||||
router.jump("/pages/hotel-manage/detail", { id: String(id) })
|
||||
}
|
||||
|
||||
const openOrderDetail = async (row: any) => {
|
||||
if (!row?.id) return
|
||||
detailShow.value = true
|
||||
detailLoading.value = true
|
||||
detail.value = null
|
||||
try {
|
||||
const res: any = await smHotelAdminApi.orderDetail(String(row.id))
|
||||
if (res?.code !== 0) {
|
||||
uni.showToast({ title: res?.msg || "详情加载失败", icon: "none" })
|
||||
detailShow.value = false
|
||||
return
|
||||
}
|
||||
detail.value = res.data || null
|
||||
} catch (e: any) {
|
||||
uni.showToast({ title: e?.msg || "详情加载失败", icon: "none" })
|
||||
detailShow.value = false
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const closeDetail = () => {
|
||||
detailShow.value = false
|
||||
detail.value = null
|
||||
}
|
||||
|
||||
const onConfirm = (row: any) => {
|
||||
uni.showModal({
|
||||
title: "确认接单",
|
||||
@@ -425,7 +614,7 @@ onShow(async () => {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 10rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.card__hotel {
|
||||
flex: 1;
|
||||
@@ -455,14 +644,41 @@ onShow(async () => {
|
||||
background: #fef3f2;
|
||||
color: #d92d20;
|
||||
}
|
||||
.card__kv {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.55;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
.card__kv .k {
|
||||
width: 120rpx;
|
||||
flex-shrink: 0;
|
||||
color: #98a2b3;
|
||||
}
|
||||
.card__kv .v {
|
||||
flex: 1;
|
||||
color: #344054;
|
||||
word-break: break-all;
|
||||
}
|
||||
.card__kv .mono {
|
||||
font-family: ui-monospace, Menlo, Consolas, monospace;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
.copyable {
|
||||
display: inline;
|
||||
}
|
||||
.copy-tip {
|
||||
margin-left: 12rpx;
|
||||
font-size: 20rpx;
|
||||
color: #2e6bf0;
|
||||
font-family: inherit;
|
||||
}
|
||||
.card__line {
|
||||
font-size: 24rpx;
|
||||
color: #344054;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.card__line.muted {
|
||||
color: #98a2b3;
|
||||
}
|
||||
.card__line.tip {
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
@@ -476,7 +692,8 @@ onShow(async () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
gap: 12rpx;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.card__amt {
|
||||
font-size: 32rpx;
|
||||
@@ -485,11 +702,13 @@ onShow(async () => {
|
||||
}
|
||||
.card__acts {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
flex-wrap: wrap;
|
||||
gap: 10rpx;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.btn {
|
||||
margin: 0;
|
||||
padding: 0 22rpx;
|
||||
padding: 0 20rpx;
|
||||
height: 56rpx;
|
||||
line-height: 56rpx;
|
||||
font-size: 24rpx;
|
||||
@@ -512,6 +731,10 @@ onShow(async () => {
|
||||
background: #d92d20;
|
||||
color: #fff;
|
||||
}
|
||||
.btn--link {
|
||||
background: rgba(46, 107, 240, 0.08);
|
||||
color: #2e6bf0;
|
||||
}
|
||||
.ord__more {
|
||||
text-align: center;
|
||||
padding: 28rpx 0 8rpx;
|
||||
@@ -524,4 +747,89 @@ onShow(async () => {
|
||||
.tab-spacer {
|
||||
height: 140rpx;
|
||||
}
|
||||
|
||||
.sheet-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
background: rgba(16, 24, 40, 0.45);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.sheet {
|
||||
width: 100%;
|
||||
max-height: 82vh;
|
||||
background: #fff;
|
||||
border-radius: 28rpx 28rpx 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sheet__hd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 28rpx 32rpx 16rpx;
|
||||
border-bottom: 1rpx solid #edf1f7;
|
||||
}
|
||||
.sheet__title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #1d2939;
|
||||
}
|
||||
.sheet__close {
|
||||
font-size: 26rpx;
|
||||
color: #667085;
|
||||
padding: 8rpx 12rpx;
|
||||
}
|
||||
.sheet__bd {
|
||||
max-height: 68vh;
|
||||
padding: 8rpx 32rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.dsec {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f2f4f7;
|
||||
}
|
||||
.dsec:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.dsec__t {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #1d2939;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.drow {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.55;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
.dk {
|
||||
width: 140rpx;
|
||||
flex-shrink: 0;
|
||||
color: #98a2b3;
|
||||
}
|
||||
.dv {
|
||||
flex: 1;
|
||||
color: #344054;
|
||||
word-break: break-all;
|
||||
}
|
||||
.dv.strong {
|
||||
font-weight: 700;
|
||||
color: #1d2939;
|
||||
}
|
||||
.dv.tip {
|
||||
color: #b42318;
|
||||
}
|
||||
.dv.mono {
|
||||
font-family: ui-monospace, Menlo, Consolas, monospace;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
.sheet__btn {
|
||||
margin-top: 20rpx;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -258,7 +258,7 @@ const submitProduct = async () => {
|
||||
form.productCode.trim() ||
|
||||
`${form.roomTypeCode}-P${String(Date.now()).slice(-4)}`
|
||||
const payload: any = {
|
||||
id: editingId.value || undefined,
|
||||
id: editingId.value ? String(editingId.value) : undefined,
|
||||
productName: form.productName.trim(),
|
||||
productCode: autoCode,
|
||||
roomTypeCode: form.roomTypeCode,
|
||||
|
||||
@@ -270,17 +270,23 @@ const submitRoom = async () => {
|
||||
uni.showToast({ title: "请填写床型", icon: "none" })
|
||||
return
|
||||
}
|
||||
if (!form.roomNumber || Number(form.roomNumber) <= 0) {
|
||||
// 物理房间数允许 0(上游常空/为 0);空才拦截。库存另在产品管理维护
|
||||
if (form.roomNumber === "" || form.roomNumber == null || Number.isNaN(Number(form.roomNumber))) {
|
||||
uni.showToast({ title: "请填写房间数", icon: "none" })
|
||||
return
|
||||
}
|
||||
if (Number(form.roomNumber) < 0) {
|
||||
uni.showToast({ title: "房间数不能为负数", icon: "none" })
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
const autoCode =
|
||||
form.roomTypeCode.trim() ||
|
||||
`${hotelCode.value || "R"}-F${String(Date.now()).slice(-6)}`
|
||||
const roomPayload: any = {
|
||||
id: editingRoomId.value || undefined,
|
||||
// 雪花 id 必须字符串,避免 Number 精度丢失
|
||||
id: editingRoomId.value ? String(editingRoomId.value) : undefined,
|
||||
roomTypeName: form.roomTypeName.trim(),
|
||||
roomTypeCode: autoCode,
|
||||
bedType: form.bedType.trim(),
|
||||
@@ -304,7 +310,11 @@ const submitRoom = async () => {
|
||||
const wantProduct = sell != null || cost != null || !!editingProductId.value
|
||||
if (wantProduct) {
|
||||
const productPayload: any = {
|
||||
id: editingProductId.value || existing?.id || undefined,
|
||||
id: editingProductId.value
|
||||
? String(editingProductId.value)
|
||||
: existing?.id != null
|
||||
? String(existing.id)
|
||||
: undefined,
|
||||
roomTypeCode,
|
||||
productCode: existing?.productCode || `${roomTypeCode}-P01`,
|
||||
productName: form.roomTypeName.trim(),
|
||||
|
||||
@@ -86,6 +86,16 @@ export default {
|
||||
method: "GET",
|
||||
params: { scopeByHotel: true, ...(params || {}) },
|
||||
header: tocOff,
|
||||
timeout: 60000,
|
||||
}),
|
||||
|
||||
/** 订单详情(含酒店/产品摘要) */
|
||||
orderDetail: (id: string | number): Promise<any> =>
|
||||
request({
|
||||
url: `/supermarket/smHotelOrder/${id}`,
|
||||
method: "GET",
|
||||
header: tocOff,
|
||||
timeout: 30000,
|
||||
}),
|
||||
|
||||
/** 本地待确认订单:确认接单 */
|
||||
|
||||
Reference in new issue
Block a user