迁移调整
This commit is contained in:
1 parent
f04d810223
commit
6b25b84ed6
11 files changed
+369
-196
No files matched your search
@@ -2,7 +2,7 @@ import request from "/@/utils/request"
|
||||
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/page",
|
||||
url: "/supermarket/smHotel/page",
|
||||
method: "get",
|
||||
params: query,
|
||||
})
|
||||
@@ -10,14 +10,14 @@ export function fetchList(query?: Object) {
|
||||
|
||||
export function getObj(id?: string) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/" + id,
|
||||
url: "/supermarket/smHotel/" + id,
|
||||
method: "get",
|
||||
})
|
||||
}
|
||||
|
||||
export function addObj(obj?: Object) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel",
|
||||
url: "/supermarket/smHotel",
|
||||
method: "post",
|
||||
data: obj,
|
||||
})
|
||||
@@ -25,7 +25,7 @@ export function addObj(obj?: Object) {
|
||||
|
||||
export function putObj(obj?: Object) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel",
|
||||
url: "/supermarket/smHotel",
|
||||
method: "put",
|
||||
data: obj,
|
||||
})
|
||||
@@ -33,7 +33,7 @@ export function putObj(obj?: Object) {
|
||||
|
||||
export function delObjs(ids?: Object) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel",
|
||||
url: "/supermarket/smHotel",
|
||||
method: "delete",
|
||||
data: ids,
|
||||
})
|
||||
@@ -42,7 +42,7 @@ export function delObjs(ids?: Object) {
|
||||
/** 提交异步导入(立刻返回 taskId) */
|
||||
export function importExcel(data: FormData) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/import",
|
||||
url: "/supermarket/smHotel/import",
|
||||
method: "post",
|
||||
data,
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
@@ -53,7 +53,7 @@ export function importExcel(data: FormData) {
|
||||
/** 轮询导入进度 */
|
||||
export function getImportStatus(taskId: string) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotel/import/status/${taskId}`,
|
||||
url: `/supermarket/smHotel/import/status/${taskId}`,
|
||||
method: "get",
|
||||
timeout: 30000,
|
||||
})
|
||||
@@ -62,7 +62,7 @@ export function getImportStatus(taskId: string) {
|
||||
/** 同步上游酒店信息(智力分销) */
|
||||
export function syncUpstreamHotel(id: string | number) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotel/upstream/sync/${id}`,
|
||||
url: `/supermarket/smHotel/upstream/sync/${id}`,
|
||||
method: "post",
|
||||
})
|
||||
}
|
||||
@@ -70,7 +70,7 @@ export function syncUpstreamHotel(id: string | number) {
|
||||
/** 批量同步上游酒店信息 */
|
||||
export function syncUpstreamHotelBatch(ids: Array<string | number>) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/upstream/sync/batch",
|
||||
url: "/supermarket/smHotel/upstream/sync/batch",
|
||||
method: "post",
|
||||
data: ids,
|
||||
})
|
||||
@@ -79,7 +79,7 @@ export function syncUpstreamHotelBatch(ids: Array<string | number>) {
|
||||
/** 上游房型列表 */
|
||||
export function upstreamRoomTypeList(data: { groupCode: string; hotelCode: string }) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/upstream/roomTypeList",
|
||||
url: "/supermarket/smHotel/upstream/roomTypeList",
|
||||
method: "post",
|
||||
data,
|
||||
})
|
||||
@@ -88,7 +88,7 @@ export function upstreamRoomTypeList(data: { groupCode: string; hotelCode: strin
|
||||
/** 上游产品列表 */
|
||||
export function upstreamProductList(data: { groupCode: string; hotelCode: string; channelCode?: string }) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/upstream/productList",
|
||||
url: "/supermarket/smHotel/upstream/productList",
|
||||
method: "post",
|
||||
data,
|
||||
})
|
||||
@@ -2,7 +2,7 @@ import request from "/@/utils/request"
|
||||
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotelCallback/page",
|
||||
url: "/supermarket/smHotelCallback/page",
|
||||
method: "get",
|
||||
params: query,
|
||||
})
|
||||
@@ -10,14 +10,14 @@ export function fetchList(query?: Object) {
|
||||
|
||||
export function getObj(id?: string | number) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotelCallback/" + id,
|
||||
url: "/supermarket/smHotelCallback/" + id,
|
||||
method: "get",
|
||||
})
|
||||
}
|
||||
|
||||
export function retryTask(id: string | number) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotelCallback/retry/${id}`,
|
||||
url: `/supermarket/smHotelCallback/retry/${id}`,
|
||||
method: "post",
|
||||
})
|
||||
}
|
||||
@@ -2,22 +2,31 @@ import request from "/@/utils/request"
|
||||
|
||||
export function getPriceConfig() {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/priceConfig",
|
||||
url: "/supermarket/smHotel/priceConfig",
|
||||
method: "get",
|
||||
})
|
||||
}
|
||||
|
||||
export function savePriceConfig(data: Object) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/priceConfig",
|
||||
url: "/supermarket/smHotel/priceConfig",
|
||||
method: "put",
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/** 按当前配置重算全部产品/日历卖价(不动订单) */
|
||||
export function resyncSellPrices() {
|
||||
return request({
|
||||
url: "/supermarket/smHotel/priceConfig/resyncSell",
|
||||
method: "post",
|
||||
timeout: 300000,
|
||||
})
|
||||
}
|
||||
|
||||
export function pullFromUpstream(id: string | number) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotel/mid/pull/${id}`,
|
||||
url: `/supermarket/smHotel/mid/pull/${id}`,
|
||||
method: "post",
|
||||
timeout: 120000,
|
||||
})
|
||||
@@ -25,7 +34,7 @@ export function pullFromUpstream(id: string | number) {
|
||||
|
||||
export function pushToDownstream(id: string | number) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotel/mid/push/${id}`,
|
||||
url: `/supermarket/smHotel/mid/push/${id}`,
|
||||
method: "post",
|
||||
timeout: 120000,
|
||||
})
|
||||
@@ -33,7 +42,7 @@ export function pushToDownstream(id: string | number) {
|
||||
|
||||
export function pullAndPush(id: string | number) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotel/mid/pullAndPush/${id}`,
|
||||
url: `/supermarket/smHotel/mid/pullAndPush/${id}`,
|
||||
method: "post",
|
||||
timeout: 180000,
|
||||
})
|
||||
@@ -41,7 +50,7 @@ export function pullAndPush(id: string | number) {
|
||||
|
||||
export function pullAndPushBatch(ids: Array<string | number>) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/pullAndPush/batch",
|
||||
url: "/supermarket/smHotel/mid/pullAndPush/batch",
|
||||
method: "post",
|
||||
data: ids,
|
||||
timeout: 300000,
|
||||
@@ -55,7 +64,7 @@ export function fetchAuthorizedHotels(query?: {
|
||||
refresh?: boolean
|
||||
}) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/authorizedHotels",
|
||||
url: "/supermarket/smHotel/mid/authorizedHotels",
|
||||
method: "get",
|
||||
params: query,
|
||||
timeout: 90000,
|
||||
@@ -64,7 +73,7 @@ export function fetchAuthorizedHotels(query?: {
|
||||
|
||||
export function importAuthorizedHotels(hotels: Array<{ hotelId: string; hotelName?: string }>) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/authorizedHotels/import",
|
||||
url: "/supermarket/smHotel/mid/authorizedHotels/import",
|
||||
method: "post",
|
||||
data: hotels,
|
||||
})
|
||||
@@ -73,7 +82,7 @@ export function importAuthorizedHotels(hotels: Array<{ hotelId: string; hotelNam
|
||||
/** 同步上游酒店列表 + Redis 差集(异步,立刻返回 logId) */
|
||||
export function syncHotelList(autoImport = true) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/syncHotelList",
|
||||
url: "/supermarket/smHotel/mid/syncHotelList",
|
||||
method: "post",
|
||||
params: { autoImport },
|
||||
timeout: 30000,
|
||||
@@ -83,7 +92,7 @@ export function syncHotelList(autoImport = true) {
|
||||
/** 查询单次同步进度 */
|
||||
export function getSyncLog(id: string | number) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotel/mid/syncLog/${id}`,
|
||||
url: `/supermarket/smHotel/mid/syncLog/${id}`,
|
||||
method: "get",
|
||||
})
|
||||
}
|
||||
@@ -91,7 +100,7 @@ export function getSyncLog(id: string | number) {
|
||||
/** 同步日志分页 */
|
||||
export function fetchSyncLogPage(query?: Object) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/syncLog/page",
|
||||
url: "/supermarket/smHotel/mid/syncLog/page",
|
||||
method: "get",
|
||||
params: query,
|
||||
})
|
||||
@@ -100,7 +109,7 @@ export function fetchSyncLogPage(query?: Object) {
|
||||
/** 日更时间配置(只读) */
|
||||
export function fetchSyncSchedule() {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/syncSchedule",
|
||||
url: "/supermarket/smHotel/mid/syncSchedule",
|
||||
method: "get",
|
||||
})
|
||||
}
|
||||
@@ -108,7 +117,7 @@ export function fetchSyncSchedule() {
|
||||
/** 差集分页 type=new|offline */
|
||||
export function fetchHotelDiff(query?: { type?: string; current?: number; size?: number }) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/hotelDiff",
|
||||
url: "/supermarket/smHotel/mid/hotelDiff",
|
||||
method: "get",
|
||||
params: query,
|
||||
})
|
||||
@@ -117,7 +126,7 @@ export function fetchHotelDiff(query?: { type?: string; current?: number; size?:
|
||||
/** 导入差集中的新增酒店 */
|
||||
export function importDiffNewHotels(limit?: number) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/hotelDiff/importNew",
|
||||
url: "/supermarket/smHotel/mid/hotelDiff/importNew",
|
||||
method: "post",
|
||||
params: { limit },
|
||||
timeout: 180000,
|
||||
@@ -127,7 +136,7 @@ export function importDiffNewHotels(limit?: number) {
|
||||
/** 手动滚一天房价(异步) */
|
||||
export function rollDailyPrice() {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/rollDailyPrice",
|
||||
url: "/supermarket/smHotel/mid/rollDailyPrice",
|
||||
method: "post",
|
||||
timeout: 30000,
|
||||
})
|
||||
@@ -136,17 +145,27 @@ export function rollDailyPrice() {
|
||||
/** 异步拉取全部启用酒店详情(房型+产品+未来房价) */
|
||||
export function pullAllActiveDetails(limit?: number) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotel/mid/pullAllActiveDetails",
|
||||
url: "/supermarket/smHotel/mid/pullAllActiveDetails",
|
||||
method: "post",
|
||||
params: limit != null ? { limit } : undefined,
|
||||
timeout: 30000,
|
||||
})
|
||||
}
|
||||
|
||||
/** 根据同步日志失败明细,只重试失败酒店(详情/滚价) */
|
||||
export function retryFailedFromLog(logId: string | number) {
|
||||
return request({
|
||||
url: "/supermarket/smHotel/mid/retryFailedFromLog",
|
||||
method: "post",
|
||||
params: { logId },
|
||||
timeout: 30000,
|
||||
})
|
||||
}
|
||||
|
||||
/** 酒店详情(房型/产品/日历预览) */
|
||||
export function getHotelDetail(id: string | number) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotel/detail/${id}`,
|
||||
url: `/supermarket/smHotel/detail/${id}`,
|
||||
method: "get",
|
||||
})
|
||||
}
|
||||
@@ -154,7 +173,7 @@ export function getHotelDetail(id: string | number) {
|
||||
/** 酒店日历分页 */
|
||||
export function fetchHotelDaily(id: string | number, query?: Object) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotel/detail/${id}/daily`,
|
||||
url: `/supermarket/smHotel/detail/${id}/daily`,
|
||||
method: "get",
|
||||
params: query,
|
||||
})
|
||||
@@ -2,7 +2,7 @@ import request from "/@/utils/request"
|
||||
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotelOrder/page",
|
||||
url: "/supermarket/smHotelOrder/page",
|
||||
method: "get",
|
||||
params: query,
|
||||
})
|
||||
@@ -10,14 +10,14 @@ export function fetchList(query?: Object) {
|
||||
|
||||
export function getObj(id?: string) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotelOrder/" + id,
|
||||
url: "/supermarket/smHotelOrder/" + id,
|
||||
method: "get",
|
||||
})
|
||||
}
|
||||
|
||||
export function cancelOrder(id: string | number, reason?: string) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotelOrder/cancel/${id}`,
|
||||
url: `/supermarket/smHotelOrder/cancel/${id}`,
|
||||
method: "post",
|
||||
data: { reason: reason || "后台取消" },
|
||||
})
|
||||
@@ -26,7 +26,7 @@ export function cancelOrder(id: string | number, reason?: string) {
|
||||
/** 后台试下单(调上游) */
|
||||
export function createAdminOrder(data: Object) {
|
||||
return request({
|
||||
url: "/canteen/canteenHotelOrder/create",
|
||||
url: "/supermarket/smHotelOrder/create",
|
||||
method: "post",
|
||||
data,
|
||||
timeout: 120000,
|
||||
@@ -35,7 +35,7 @@ export function createAdminOrder(data: Object) {
|
||||
|
||||
export function compensateOrder(id: string | number) {
|
||||
return request({
|
||||
url: `/canteen/canteenHotelOrder/compensate/${id}`,
|
||||
url: `/supermarket/smHotelOrder/compensate/${id}`,
|
||||
method: "post",
|
||||
})
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-card shadow="never" class="max-w-xl">
|
||||
<template #header>
|
||||
<span>酒店全局加价配置</span>
|
||||
</template>
|
||||
<el-alert
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-4"
|
||||
title="上游底价 × (1 + 加价%/100) = 推给下游的卖价。例如上游 100 元、加价 15%,下游看到 115 元。"
|
||||
/>
|
||||
<el-form v-loading="loading" label-width="120px">
|
||||
<el-form-item label="加价百分比">
|
||||
<el-input-number v-model="form.markupPercent" :min="0" :max="500" :precision="2" :step="1" />
|
||||
<span class="ml-2 text-gray-500">%</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="选填" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" v-auth="'canteen_canteenHotelPrice_edit'" @click="save">
|
||||
保存
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemCanteenHotelPrice">
|
||||
import { getPriceConfig, savePriceConfig } from "/@/api/canteen/canteenHotelMid"
|
||||
import { useMessage } from "/@/hooks/message"
|
||||
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const form = reactive({
|
||||
markupPercent: 0,
|
||||
remark: "",
|
||||
})
|
||||
|
||||
const load = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res: any = await getPriceConfig()
|
||||
const data = res?.data || {}
|
||||
form.markupPercent = Number(data.markupPercent || 0)
|
||||
form.remark = data.remark || ""
|
||||
} catch (err: any) {
|
||||
useMessage().error(err?.msg || "加载失败")
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
saving.value = true
|
||||
try {
|
||||
await savePriceConfig({
|
||||
markupPercent: form.markupPercent,
|
||||
remark: form.remark,
|
||||
})
|
||||
useMessage().success("保存成功")
|
||||
load()
|
||||
} catch (err: any) {
|
||||
useMessage().error(err?.msg || "保存失败")
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
+10
-14
@@ -4,10 +4,6 @@
|
||||
<el-descriptions v-if="hotel" :column="2" border class="mb-4">
|
||||
<el-descriptions-item label="酒店名称">{{ hotel.hotelName || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="酒店编码">{{ hotel.hotelCode || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="集团编码">{{ hotel.groupCode || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="外部ID">{{ hotel.sourceId || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="外部集团ID">{{ hotel.sGroupId || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="外部酒店ID">{{ hotel.sHotelId || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="来源Sheet">{{ hotel.sheetName || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ hotel.remark || "-" }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
@@ -50,7 +46,7 @@
|
||||
<el-table-column prop="priceCode" label="价格码" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="110" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button text type="primary" v-auth="'canteen_canteenHotel_edit'" @click="openOrderByProduct(scope.row)">
|
||||
<el-button text type="primary" v-auth="'supermarket_smHotel_edit'" @click="openOrderByProduct(scope.row)">
|
||||
试下单
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -83,7 +79,7 @@
|
||||
<el-button
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'canteen_canteenHotel_edit'"
|
||||
v-auth="'supermarket_smHotel_edit'"
|
||||
:disabled="scope.row.dailyStatus !== 1 || !(scope.row.stock > 0)"
|
||||
@click="openOrderByDaily(scope.row)"
|
||||
>
|
||||
@@ -127,7 +123,7 @@
|
||||
<el-input-number v-model="orderForm.count" :min="1" :max="10" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系人" required>
|
||||
<el-input v-model="orderForm.contactName" placeholder="姓名" />
|
||||
<el-input v-model="orderForm.contactName" placeholder="中文姓名,如:张三" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" required>
|
||||
<el-input v-model="orderForm.contactPhone" placeholder="手机号" />
|
||||
@@ -157,7 +153,7 @@
|
||||
plain
|
||||
size="small"
|
||||
:loading="cancelLoading"
|
||||
v-auth="'canteen_canteenHotelOrder_cancel'"
|
||||
v-auth="'supermarket_smHotelOrder_cancel'"
|
||||
:disabled="!lastOrder.platformNo || lastOrder.orderStatus === 'CANCELLED'"
|
||||
@click="handleCancelLast"
|
||||
>
|
||||
@@ -167,7 +163,7 @@
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="orderVisible = false">关闭</el-button>
|
||||
<el-button type="primary" :loading="orderSubmitting" v-auth="'canteen_canteenHotel_edit'" @click="submitOrder">
|
||||
<el-button type="primary" :loading="orderSubmitting" v-auth="'supermarket_smHotel_edit'" @click="submitOrder">
|
||||
提交下单
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -175,9 +171,9 @@
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CanteenHotelDetail">
|
||||
import { getHotelDetail, fetchHotelDaily } from "/@/api/canteen/canteenHotelMid"
|
||||
import { createAdminOrder, cancelOrder } from "/@/api/canteen/canteenHotelOrder"
|
||||
<script setup lang="ts" name="SmHotelDetail">
|
||||
import { getHotelDetail, fetchHotelDaily } from "/@/api/supermarket/smHotelMid"
|
||||
import { createAdminOrder, cancelOrder } from "/@/api/supermarket/smHotelOrder"
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message"
|
||||
|
||||
const visible = ref(false)
|
||||
@@ -207,7 +203,7 @@ const orderForm = reactive({
|
||||
checkInDate: "",
|
||||
checkOutDate: "",
|
||||
count: 1,
|
||||
contactName: "测试用户",
|
||||
contactName: "张三",
|
||||
contactPhone: "13800138000",
|
||||
})
|
||||
|
||||
@@ -338,7 +334,7 @@ const openOrderByDaily = (daily: any) => {
|
||||
const fillOrderForm = ( partial: Partial<typeof orderForm>) => {
|
||||
Object.assign(orderForm, {
|
||||
count: 1,
|
||||
contactName: orderForm.contactName || "测试用户",
|
||||
contactName: orderForm.contactName || "张三",
|
||||
contactPhone: orderForm.contactPhone || "13800138000",
|
||||
...partial,
|
||||
})
|
||||
@@ -8,20 +8,8 @@
|
||||
@close="closeDialog"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px" v-loading="loading">
|
||||
<el-form-item label="外部ID" prop="sourceId">
|
||||
<el-input v-model="form.sourceId" placeholder="Excel 中的 id,选填" maxlength="64" />
|
||||
</el-form-item>
|
||||
<el-form-item label="集团编码" prop="groupCode">
|
||||
<el-input v-model="form.groupCode" placeholder="group_code" maxlength="64" />
|
||||
</el-form-item>
|
||||
<el-form-item label="酒店编码" prop="hotelCode">
|
||||
<el-input v-model="form.hotelCode" placeholder="hotel_code" maxlength="64" />
|
||||
</el-form-item>
|
||||
<el-form-item label="外部集团ID" prop="sGroupId">
|
||||
<el-input v-model="form.sGroupId" placeholder="s_group_id" maxlength="64" />
|
||||
</el-form-item>
|
||||
<el-form-item label="外部酒店ID" prop="sHotelId">
|
||||
<el-input v-model="form.sHotelId" placeholder="s_hotel_id" maxlength="64" />
|
||||
<el-input v-model="form.hotelCode" placeholder="hotel_code(上游酒店ID)" maxlength="64" />
|
||||
</el-form-item>
|
||||
<el-form-item label="酒店名称" prop="hotelName">
|
||||
<el-input v-model="form.hotelName" placeholder="hotel_name" maxlength="256" />
|
||||
@@ -37,10 +25,10 @@
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CanteenHotelForm">
|
||||
<script setup lang="ts" name="SmHotelForm">
|
||||
import type { FormInstance } from "element-plus"
|
||||
import { useMessage } from "/@/hooks/message"
|
||||
import { addObj, putObj, getObj } from "/@/api/canteen/canteenHotel"
|
||||
import { addObj, putObj, getObj } from "/@/api/supermarket/smHotel"
|
||||
|
||||
const emit = defineEmits(["refresh"])
|
||||
|
||||
@@ -50,13 +38,14 @@ const formRef = ref<FormInstance>()
|
||||
|
||||
const defaultForm = () => ({
|
||||
id: undefined as string | undefined,
|
||||
sourceId: "",
|
||||
groupCode: "",
|
||||
hotelCode: "",
|
||||
sGroupId: "",
|
||||
sHotelId: "",
|
||||
hotelName: "",
|
||||
remark: "",
|
||||
// 库字段保留,编辑时带回避免被清空;页面不展示
|
||||
sourceId: "",
|
||||
groupCode: "",
|
||||
sGroupId: "",
|
||||
sHotelId: "",
|
||||
})
|
||||
|
||||
const form = reactive(defaultForm())
|
||||
@@ -87,13 +76,13 @@ const openDialog = async (id?: string) => {
|
||||
const data = res.data
|
||||
if (!data) return
|
||||
form.id = data.id
|
||||
form.sourceId = data.sourceId || ""
|
||||
form.groupCode = data.groupCode || ""
|
||||
form.hotelCode = data.hotelCode || ""
|
||||
form.sGroupId = data.sGroupId || ""
|
||||
form.sHotelId = data.sHotelId || ""
|
||||
form.hotelName = data.hotelName || ""
|
||||
form.remark = data.remark || ""
|
||||
form.sourceId = data.sourceId || ""
|
||||
form.groupCode = data.groupCode || ""
|
||||
form.sGroupId = data.sGroupId || ""
|
||||
form.sHotelId = data.sHotelId || ""
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || "加载失败")
|
||||
} finally {
|
||||
@@ -113,13 +102,13 @@ const submitForm = async () => {
|
||||
try {
|
||||
const payload = {
|
||||
id: form.id,
|
||||
sourceId: form.sourceId?.trim() || null,
|
||||
groupCode: form.groupCode?.trim() || null,
|
||||
hotelCode: form.hotelCode?.trim() || null,
|
||||
sGroupId: form.sGroupId?.trim() || null,
|
||||
sHotelId: form.sHotelId?.trim() || null,
|
||||
hotelName: form.hotelName?.trim() || null,
|
||||
remark: form.remark?.trim() || null,
|
||||
sourceId: form.sourceId?.trim() || null,
|
||||
groupCode: form.groupCode?.trim() || null,
|
||||
sGroupId: form.sGroupId?.trim() || null,
|
||||
sHotelId: form.sHotelId?.trim() || null,
|
||||
}
|
||||
form.id ? await putObj(payload) : await addObj(payload)
|
||||
useMessage().success(form.id ? "修改成功" : "新增成功")
|
||||
+100
-34
@@ -9,12 +9,6 @@
|
||||
<el-form-item label="酒店编码" prop="hotelCode">
|
||||
<el-input v-model="state.queryForm.hotelCode" placeholder="hotel_code" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="集团编码" prop="groupCode">
|
||||
<el-input v-model="state.queryForm.groupCode" placeholder="group_code" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="外部ID" prop="sourceId">
|
||||
<el-input v-model="state.queryForm.sourceId" placeholder="Excel 中的 id" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="上游状态" prop="upstreamStatus">
|
||||
<el-select v-model="state.queryForm.upstreamStatus" clearable placeholder="全部" style="width: 120px">
|
||||
<el-option label="启用" value="ACTIVE" />
|
||||
@@ -33,7 +27,7 @@
|
||||
icon="folder-add"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
v-auth="'canteen_canteenHotel_add'"
|
||||
v-auth="'supermarket_smHotel_add'"
|
||||
@click="formDialogRef.openDialog()"
|
||||
>
|
||||
新 增
|
||||
@@ -43,7 +37,7 @@
|
||||
:disabled="multiple"
|
||||
icon="Delete"
|
||||
type="primary"
|
||||
v-auth="'canteen_canteenHotel_del'"
|
||||
v-auth="'supermarket_smHotel_del'"
|
||||
@click="handleDelete(selectObjs)"
|
||||
>
|
||||
删除
|
||||
@@ -53,7 +47,7 @@
|
||||
type="primary"
|
||||
class="ml10"
|
||||
:loading="syncListLoading"
|
||||
v-auth="'canteen_canteenHotel_edit'"
|
||||
v-auth="'supermarket_smHotel_edit'"
|
||||
@click="handleSyncHotelList"
|
||||
>
|
||||
同步上游酒店
|
||||
@@ -63,7 +57,7 @@
|
||||
icon="Document"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
v-auth="'canteen_canteenHotel_view'"
|
||||
v-auth="'supermarket_smHotel_view'"
|
||||
@click="openSyncLogDialog"
|
||||
>
|
||||
同步日志
|
||||
@@ -74,7 +68,7 @@
|
||||
type="primary"
|
||||
class="ml10"
|
||||
:loading="authHotelLoading"
|
||||
v-auth="'canteen_canteenHotel_view'"
|
||||
v-auth="'supermarket_smHotel_view'"
|
||||
@click="openAuthorizedHotels"
|
||||
>
|
||||
授权酒店 / 差集
|
||||
@@ -83,7 +77,7 @@
|
||||
plain
|
||||
icon="Upload"
|
||||
class="ml10"
|
||||
v-auth="'canteen_canteenHotel_import'"
|
||||
v-auth="'supermarket_smHotel_import'"
|
||||
@click="openImport"
|
||||
>
|
||||
Excel(兼容)
|
||||
@@ -94,7 +88,7 @@
|
||||
type="warning"
|
||||
class="ml10"
|
||||
:loading="syncListLoading"
|
||||
v-auth="'canteen_canteenHotel_edit'"
|
||||
v-auth="'supermarket_smHotel_edit'"
|
||||
@click="handlePullAllActiveDetails"
|
||||
>
|
||||
拉取全部启用详情
|
||||
@@ -106,7 +100,7 @@
|
||||
class="ml10"
|
||||
:disabled="multiple"
|
||||
:loading="syncLoading"
|
||||
v-auth="'canteen_canteenHotel_edit'"
|
||||
v-auth="'supermarket_smHotel_edit'"
|
||||
@click="handleSyncBatch"
|
||||
>
|
||||
拉取选中详情
|
||||
@@ -118,7 +112,7 @@
|
||||
class="ml10"
|
||||
:disabled="multiple"
|
||||
:loading="pushLoading"
|
||||
v-auth="'canteen_canteenHotel_push'"
|
||||
v-auth="'supermarket_smHotel_push'"
|
||||
@click="handlePullPushBatch"
|
||||
>
|
||||
拉取并推下游
|
||||
@@ -136,11 +130,7 @@
|
||||
>
|
||||
<el-table-column type="selection" width="40" align="center" />
|
||||
<el-table-column type="index" label="#" width="50" />
|
||||
<el-table-column prop="sourceId" label="外部ID" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="groupCode" label="集团编码" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="hotelCode" label="酒店编码" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="sGroupId" label="外部集团ID" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="sHotelId" label="外部酒店ID" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="hotelName" label="酒店名称" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="upstreamStatus" label="上游状态" width="100">
|
||||
<template #default="scope">
|
||||
@@ -148,6 +138,15 @@
|
||||
<el-tag v-else type="success" size="small">启用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="roomCount" label="房型数" width="90" align="center">
|
||||
<template #default="scope">{{ scope.row.roomCount ?? 0 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="productCount" label="产品数" width="90" align="center">
|
||||
<template #default="scope">{{ scope.row.productCount ?? 0 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="dailyCount" label="日历数" width="90" align="center">
|
||||
<template #default="scope">{{ scope.row.dailyCount ?? 0 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sheetName" label="来源Sheet" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="380" fixed="right">
|
||||
<template #default="scope">
|
||||
@@ -155,7 +154,7 @@
|
||||
icon="view"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'canteen_canteenHotel_view'"
|
||||
v-auth="'supermarket_smHotel_view'"
|
||||
@click="detailRef.open(scope.row.id)"
|
||||
>
|
||||
详情
|
||||
@@ -164,7 +163,7 @@
|
||||
icon="edit-pen"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'canteen_canteenHotel_edit'"
|
||||
v-auth="'supermarket_smHotel_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
@@ -173,7 +172,7 @@
|
||||
icon="Refresh"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'canteen_canteenHotel_edit'"
|
||||
v-auth="'supermarket_smHotel_edit'"
|
||||
:loading="syncingId === scope.row.id"
|
||||
@click="handleSyncOne(scope.row.id)"
|
||||
>
|
||||
@@ -183,7 +182,7 @@
|
||||
icon="Promotion"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'canteen_canteenHotel_push'"
|
||||
v-auth="'supermarket_smHotel_push'"
|
||||
:loading="pushingId === scope.row.id"
|
||||
@click="handlePullPushOne(scope.row.id)"
|
||||
>
|
||||
@@ -193,7 +192,7 @@
|
||||
icon="delete"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'canteen_canteenHotel_del'"
|
||||
v-auth="'supermarket_smHotel_del'"
|
||||
@click="handleDelete([scope.row.id])"
|
||||
>
|
||||
删除
|
||||
@@ -246,16 +245,18 @@
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-3"
|
||||
title="主路径:点「同步上游酒店」强制 getHotelList,用 Redis 差集算出新增/下线,并可自动导入新增。Excel 仅兼容旧数据。详情靠「拉取选中/全部启用详情」或每日滚一天 + 回调。"
|
||||
title="主路径:点「同步上游并比对」拉取 getHotelList、算差集,并自动按批循环导入全部新增(无需再手点)。「补导剩余新增」仅在自动导入中断时用。详情靠拉取详情 / 日滚价 / 回调。"
|
||||
/>
|
||||
<div class="mb-3 flex flex-wrap items-center gap-2">
|
||||
<el-button type="primary" :loading="syncListLoading" v-auth="'canteen_canteenHotel_edit'" @click="handleSyncHotelList">
|
||||
<el-button type="primary" :loading="syncListLoading" v-auth="'supermarket_smHotel_edit'" @click="handleSyncHotelList">
|
||||
同步上游并比对
|
||||
</el-button>
|
||||
<el-tag v-if="diffSummary" type="success">新增 {{ diffSummary.newCount ?? 0 }}</el-tag>
|
||||
<el-tag v-if="diffSummary" type="warning">下线 {{ diffSummary.offlineCount ?? 0 }}</el-tag>
|
||||
<el-tag v-if="diffSummary" type="info">上游 {{ diffSummary.upstreamCount ?? 0 }} / 本地 {{ diffSummary.localCount ?? 0 }}</el-tag>
|
||||
<el-button :loading="diffImporting" v-auth="'canteen_canteenHotel_add'" @click="handleImportDiffNew">导入全部新增</el-button>
|
||||
<el-button :loading="diffImporting" v-auth="'supermarket_smHotel_add'" @click="handleImportDiffNew">
|
||||
补导剩余新增
|
||||
</el-button>
|
||||
</div>
|
||||
<el-tabs v-model="authTab" @tab-change="onAuthTabChange">
|
||||
<el-tab-pane label="授权列表" name="all" />
|
||||
@@ -310,7 +311,7 @@
|
||||
v-if="authTab === 'all'"
|
||||
type="primary"
|
||||
:loading="authHotelImporting"
|
||||
v-auth="'canteen_canteenHotel_add'"
|
||||
v-auth="'supermarket_smHotel_add'"
|
||||
@click="importSelectedAuthHotels"
|
||||
>
|
||||
导入选中到本地
|
||||
@@ -425,14 +426,27 @@
|
||||
<el-dialog v-model="failDetailVisible" title="失败明细" width="720px" destroy-on-close>
|
||||
<div class="mb-2 text-sm text-gray-600">{{ failDetailSummary }}</div>
|
||||
<pre class="sync-fail-detail">{{ failDetailText || "无明细" }}</pre>
|
||||
<template #footer>
|
||||
<div class="flex justify-end gap-2">
|
||||
<el-button @click="failDetailVisible = false">关闭</el-button>
|
||||
<el-button
|
||||
v-if="canRetryFailed"
|
||||
type="primary"
|
||||
:loading="retryFailedLoading"
|
||||
@click="onRetryFailed"
|
||||
>
|
||||
只重试这些失败酒店
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemCanteenHotel">
|
||||
<script setup lang="ts" name="systemSmHotel">
|
||||
import type { UploadFile, UploadInstance } from "element-plus"
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table"
|
||||
import { fetchList, delObjs, importExcel, getImportStatus } from "/@/api/canteen/canteenHotel"
|
||||
import { fetchList, delObjs, importExcel, getImportStatus } from "/@/api/supermarket/smHotel"
|
||||
import {
|
||||
pullFromUpstream,
|
||||
pullAndPush,
|
||||
@@ -446,7 +460,8 @@ import {
|
||||
fetchSyncLogPage,
|
||||
fetchSyncSchedule,
|
||||
pullAllActiveDetails,
|
||||
} from "/@/api/canteen/canteenHotelMid"
|
||||
retryFailedFromLog,
|
||||
} from "/@/api/supermarket/smHotelMid"
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message"
|
||||
|
||||
const FormDialog = defineAsyncComponent(() => import("./form.vue"))
|
||||
@@ -502,13 +517,61 @@ const syncScheduleHint = ref("日更时间可在 Nacos/配置 shangyou-hotel.hot
|
||||
const failDetailVisible = ref(false)
|
||||
const failDetailText = ref("")
|
||||
const failDetailSummary = ref("")
|
||||
const failDetailLogId = ref<string | number | null>(null)
|
||||
const failDetailSyncType = ref("")
|
||||
const retryFailedLoading = ref(false)
|
||||
|
||||
const canRetryFailed = computed(
|
||||
() =>
|
||||
!!failDetailLogId.value &&
|
||||
!!failDetailText.value &&
|
||||
(failDetailSyncType.value === "DETAIL_PULL" || failDetailSyncType.value === "PRICE_ROLL"),
|
||||
)
|
||||
|
||||
const openFailDetail = (row: any) => {
|
||||
failDetailSummary.value = row.errorMsg || `失败 ${row.failCount || 0} 家`
|
||||
failDetailText.value = row.failDetail || row.errorMsg || ""
|
||||
failDetailLogId.value = row.id || null
|
||||
failDetailSyncType.value = row.syncType || ""
|
||||
failDetailVisible.value = true
|
||||
}
|
||||
|
||||
const onRetryFailed = async () => {
|
||||
if (!failDetailLogId.value) return
|
||||
try {
|
||||
await useMessageBox().confirm(
|
||||
failDetailSyncType.value === "PRICE_ROLL"
|
||||
? "将只重试本日志失败明细中的酒店滚价(同日),确定?"
|
||||
: "将只重试本日志失败明细中的酒店详情拉取,确定?",
|
||||
)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
retryFailedLoading.value = true
|
||||
try {
|
||||
const res: any = await retryFailedFromLog(failDetailLogId.value)
|
||||
const data = res?.data || {}
|
||||
if (!data.logId) {
|
||||
useMessage().error(res?.msg || data.message || "提交重试失败")
|
||||
return
|
||||
}
|
||||
failDetailVisible.value = false
|
||||
syncProgressVisible.value = true
|
||||
syncProgressPct.value = 1
|
||||
syncProgressStatus.value = ""
|
||||
syncProgressText.value = res?.msg || "已提交失败重试…"
|
||||
useMessage().success(res?.msg || "已提交失败重试")
|
||||
pollSyncLog(data.logId)
|
||||
if (syncLogVisible.value) {
|
||||
loadSyncLogs(syncLogPage.value)
|
||||
}
|
||||
} catch (err: any) {
|
||||
useMessage().error(err?.msg || err?.message || "提交重试失败")
|
||||
} finally {
|
||||
retryFailedLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const stopSyncPoll = () => {
|
||||
if (syncPollTimer != null) {
|
||||
clearInterval(syncPollTimer)
|
||||
@@ -551,15 +614,18 @@ const applySyncLogUi = (log: any) => {
|
||||
stopSyncPoll()
|
||||
if (log.syncType === "DETAIL_PULL") {
|
||||
useMessage().success(
|
||||
`全量详情完成:成功 ${log.successCount || 0},失败 ${log.failCount || 0},跳过 ${log.skipCount || 0}`,
|
||||
`${log.triggerType === "RETRY" ? "失败重试" : "全量详情"}完成:成功 ${log.successCount || 0},失败 ${log.failCount || 0},跳过 ${log.skipCount || 0}`,
|
||||
)
|
||||
if (log.failCount > 0 && (log.failDetail || log.errorMsg)) {
|
||||
openFailDetail(log)
|
||||
}
|
||||
} else if (log.syncType === "PRICE_ROLL") {
|
||||
useMessage().success(
|
||||
`滚价完成:成功 ${log.successCount || 0},失败 ${log.failCount || 0}`,
|
||||
`${log.triggerType === "RETRY" ? "滚价失败重试" : "滚价"}完成:成功 ${log.successCount || 0},失败 ${log.failCount || 0}`,
|
||||
)
|
||||
if (log.triggerType === "RETRY" && log.failCount > 0 && (log.failDetail || log.errorMsg)) {
|
||||
openFailDetail(log)
|
||||
}
|
||||
} else {
|
||||
useMessage().success(
|
||||
`同步完成:上游 ${log.upstreamCount || 0},新增 ${log.newCount || 0},下线 ${log.offlineCount || 0},导入 ${log.imported || 0},停用 ${log.offlineMarked || 0}`,
|
||||
@@ -701,7 +767,7 @@ const loadHotelDiff = async (type: string, page?: number) => {
|
||||
const handleSyncHotelList = async () => {
|
||||
try {
|
||||
await useMessageBox().confirm(
|
||||
"将异步同步上游授权酒店列表并做差集(可达十几万,建议低峰)。确定后立即返回进度条,后台继续执行;自动导入「新增」。",
|
||||
"将异步同步上游授权酒店列表并做差集(可达十几万,建议低峰)。确定后立即返回进度;后台会自动按批循环导入全部新增,无需再手动导入。",
|
||||
)
|
||||
} catch {
|
||||
return
|
||||
+4
-4
@@ -67,12 +67,12 @@
|
||||
<el-table-column prop="finishTime" label="完成时间" min-width="160" />
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button text type="primary" v-auth="'canteen_canteenHotelCallback_view'" @click="openDetail(scope.row)">详情</el-button>
|
||||
<el-button text type="primary" v-auth="'supermarket_smHotelCallback_view'" @click="openDetail(scope.row)">详情</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === 'FAILED' || scope.row.status === 'SKIPPED' || scope.row.status === 'SUCCESS'"
|
||||
text
|
||||
type="warning"
|
||||
v-auth="'canteen_canteenHotelCallback_retry'"
|
||||
v-auth="'supermarket_smHotelCallback_retry'"
|
||||
@click="handleRetry(scope.row)"
|
||||
>
|
||||
重试
|
||||
@@ -106,9 +106,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemCanteenHotelCallback">
|
||||
<script setup lang="ts" name="systemSmHotelCallback">
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table"
|
||||
import { fetchList, getObj, retryTask } from "/@/api/canteen/canteenHotelCallback"
|
||||
import { fetchList, getObj, retryTask } from "/@/api/supermarket/smHotelCallback"
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message"
|
||||
|
||||
const queryRef = ref()
|
||||
+4
-4
@@ -90,7 +90,7 @@
|
||||
v-if="scope.row.compensateStatus === 'WAITING' || scope.row.compensateStatus === 'NEED_MANUAL' || scope.row.orderStatus === 'PENDING_CONFIRM'"
|
||||
text
|
||||
type="warning"
|
||||
v-auth="'canteen_canteenHotelOrder_view'"
|
||||
v-auth="'supermarket_smHotelOrder_view'"
|
||||
@click="handleCompensate(scope.row)"
|
||||
>
|
||||
补偿查询
|
||||
@@ -99,7 +99,7 @@
|
||||
v-if="scope.row.orderStatus !== 'CANCELLED' && scope.row.platformNo"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'canteen_canteenHotelOrder_cancel'"
|
||||
v-auth="'supermarket_smHotelOrder_cancel'"
|
||||
@click="handleCancel(scope.row)"
|
||||
>
|
||||
取消
|
||||
@@ -112,9 +112,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemCanteenHotelOrder">
|
||||
<script setup lang="ts" name="systemSmHotelOrder">
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table"
|
||||
import { fetchList, cancelOrder, compensateOrder } from "/@/api/canteen/canteenHotelOrder"
|
||||
import { fetchList, cancelOrder, compensateOrder } from "/@/api/supermarket/smHotelOrder"
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message"
|
||||
|
||||
const queryRef = ref()
|
||||
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-card shadow="never" class="max-w-xl">
|
||||
<template #header>
|
||||
<span>酒店全局售价调整</span>
|
||||
</template>
|
||||
<el-alert type="info" :closable="false" show-icon class="mb-4" :title="hintTitle" />
|
||||
<el-alert
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-4"
|
||||
title="保存配置不会立刻改库存卖价。需要时再点「保存并立刻全局同步」:按底价重算全部产品/日历卖价,历史订单价格不变。"
|
||||
/>
|
||||
<el-form v-loading="loading" label-width="120px">
|
||||
<el-form-item label="调整方式">
|
||||
<el-radio-group v-model="adjustMode">
|
||||
<el-radio-button label="MARKUP">加价</el-radio-button>
|
||||
<el-radio-button label="DISCOUNT">折扣</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="adjustMode === 'DISCOUNT' ? '折扣百分比' : '加价百分比'">
|
||||
<el-input-number
|
||||
v-model="adjustPercent"
|
||||
:min="0"
|
||||
:max="adjustMode === 'DISCOUNT' ? 99.99 : 500"
|
||||
:precision="2"
|
||||
:step="1"
|
||||
/>
|
||||
<span class="ml-2 text-gray-500">%</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="预览">
|
||||
<span class="text-gray-600">{{ previewText }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="选填" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :loading="saving" v-auth="'supermarket_smHotelPrice_edit'" @click="saveOnly">
|
||||
仅保存配置
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="savingAndSync"
|
||||
v-auth="'supermarket_smHotelPrice_edit'"
|
||||
class="ml-2"
|
||||
@click="saveAndResync"
|
||||
>
|
||||
保存并立刻全局同步
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="lastSyncMsg">
|
||||
<span class="text-sm text-gray-500">{{ lastSyncMsg }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemSmHotelPrice">
|
||||
import { getPriceConfig, savePriceConfig, resyncSellPrices } from "/@/api/supermarket/smHotelMid"
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message"
|
||||
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const savingAndSync = ref(false)
|
||||
const lastSyncMsg = ref("")
|
||||
/** MARKUP=加价,DISCOUNT=折扣;落库时折扣存为负数 markupPercent */
|
||||
const adjustMode = ref<"MARKUP" | "DISCOUNT">("MARKUP")
|
||||
const adjustPercent = ref(0)
|
||||
const form = reactive({
|
||||
remark: "",
|
||||
})
|
||||
|
||||
const hintTitle = computed(() => {
|
||||
if (adjustMode.value === "DISCOUNT") {
|
||||
return "卖价 = 上游底价 × (1 - 折扣%/100)。例如上游 100 元、折扣 10%,下游看到 90 元。"
|
||||
}
|
||||
return "卖价 = 上游底价 × (1 + 加价%/100)。例如上游 100 元、加价 15%,下游看到 115 元。"
|
||||
})
|
||||
|
||||
const previewText = computed(() => {
|
||||
const p = Number(adjustPercent.value || 0)
|
||||
const sample = 100
|
||||
let sell = sample
|
||||
if (adjustMode.value === "DISCOUNT") {
|
||||
sell = sample * (1 - p / 100)
|
||||
} else {
|
||||
sell = sample * (1 + p / 100)
|
||||
}
|
||||
sell = Math.round(sell * 100) / 100
|
||||
const modeLabel = adjustMode.value === "DISCOUNT" ? `折扣 ${p}%` : `加价 ${p}%`
|
||||
return `若上游底价 100 元,当前${modeLabel} → 下游卖价约 ${sell} 元`
|
||||
})
|
||||
|
||||
const toSignedPercent = () => {
|
||||
const p = Number(adjustPercent.value || 0)
|
||||
return adjustMode.value === "DISCOUNT" ? -Math.abs(p) : Math.abs(p)
|
||||
}
|
||||
|
||||
const validateBeforeSave = () => {
|
||||
if (adjustMode.value === "DISCOUNT" && Number(adjustPercent.value || 0) >= 100) {
|
||||
useMessage().warning("折扣须小于 100%")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const load = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res: any = await getPriceConfig()
|
||||
const data = res?.data || {}
|
||||
const signed = Number(data.markupPercent || 0)
|
||||
if (signed < 0) {
|
||||
adjustMode.value = "DISCOUNT"
|
||||
adjustPercent.value = Math.abs(signed)
|
||||
} else {
|
||||
adjustMode.value = "MARKUP"
|
||||
adjustPercent.value = Math.abs(signed)
|
||||
}
|
||||
form.remark = data.remark || ""
|
||||
} catch (err: any) {
|
||||
useMessage().error(err?.msg || "加载失败")
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const doSave = async () => {
|
||||
await savePriceConfig({
|
||||
markupPercent: toSignedPercent(),
|
||||
remark: form.remark,
|
||||
})
|
||||
}
|
||||
|
||||
const saveOnly = async () => {
|
||||
if (!validateBeforeSave()) return
|
||||
saving.value = true
|
||||
try {
|
||||
await doSave()
|
||||
useMessage().success("配置已保存(尚未重算库存卖价)")
|
||||
load()
|
||||
} catch (err: any) {
|
||||
useMessage().error(err?.msg || "保存失败")
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const saveAndResync = async () => {
|
||||
if (!validateBeforeSave()) return
|
||||
try {
|
||||
await useMessageBox().confirm(
|
||||
"将先保存配置,再按底价重算全部产品与日历卖价。历史订单价格不会改动。数据量大时可能需要几十秒,是否继续?"
|
||||
)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
savingAndSync.value = true
|
||||
lastSyncMsg.value = ""
|
||||
try {
|
||||
await doSave()
|
||||
const res: any = await resyncSellPrices()
|
||||
const data = res?.data || {}
|
||||
lastSyncMsg.value = `已同步:产品 ${data.productUpdated ?? 0} 条,日历 ${data.dailyUpdated ?? 0} 条(调整 ${data.markupPercent ?? 0}%)`
|
||||
useMessage().success(lastSyncMsg.value)
|
||||
load()
|
||||
} catch (err: any) {
|
||||
useMessage().error(err?.msg || "保存或同步失败")
|
||||
} finally {
|
||||
savingAndSync.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
Reference in new issue
Block a user