|
|
@@ -167,40 +167,42 @@
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<template v-for="(item, idx) in form.specialList" :key="item.name + '-' + idx">
|
|
|
- <el-form-item :label="item.name">
|
|
|
- <el-radio-group v-model="item.allDay">
|
|
|
- <el-radio label="allDay" :disabled="!!specialAllDayDisabledMap[item.name]"> 全天 </el-radio>
|
|
|
- <el-radio label="notAllDay"> 非全天 </el-radio>
|
|
|
- </el-radio-group>
|
|
|
- </el-form-item>
|
|
|
- <div v-if="item.allDay === 'notAllDay'" class="special-time-row">
|
|
|
- <el-form-item label="开始时间" label-width="450px">
|
|
|
- <el-time-picker
|
|
|
- v-model="item.startTime"
|
|
|
- format="HH:mm"
|
|
|
- value-format="HH:mm"
|
|
|
- placeholder="选择开始时间"
|
|
|
- style="width: 160px"
|
|
|
- :disabled-hours="() => specialDisabledHours()"
|
|
|
- :disabled-minutes="(h: number) => specialDisabledMinutes(h)"
|
|
|
- @visible-change="v => v && initSpecialPickerRange(item.name, 'start')"
|
|
|
- @change="() => onSpecialTimeChange(item.name, 'start')"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="结束时间" label-width="450px">
|
|
|
- <el-time-picker
|
|
|
- v-model="item.endTime"
|
|
|
- format="HH:mm"
|
|
|
- value-format="HH:mm"
|
|
|
- placeholder="选择结束时间(次日)"
|
|
|
- style="width: 160px"
|
|
|
- :disabled-hours="() => specialDisabledHours()"
|
|
|
- :disabled-minutes="(h: number) => specialDisabledMinutes(h)"
|
|
|
- @visible-change="v => v && initSpecialPickerRange(item.name, 'end')"
|
|
|
- @change="() => onSpecialTimeChange(item.name, 'end')"
|
|
|
- />
|
|
|
+ <template v-if="showSpecialRowTimeUI(item)">
|
|
|
+ <el-form-item :label="item.name">
|
|
|
+ <el-radio-group v-model="item.allDay">
|
|
|
+ <el-radio label="allDay" :disabled="!!specialAllDayDisabledMap[item.name]"> 全天 </el-radio>
|
|
|
+ <el-radio label="notAllDay"> 非全天 </el-radio>
|
|
|
+ </el-radio-group>
|
|
|
</el-form-item>
|
|
|
- </div>
|
|
|
+ <div v-if="item.allDay === 'notAllDay'" class="special-time-row" style="display: flex">
|
|
|
+ <el-form-item label="时间设置" label-width="450px">
|
|
|
+ <el-time-picker
|
|
|
+ v-model="item.startTime"
|
|
|
+ format="HH:mm"
|
|
|
+ value-format="HH:mm"
|
|
|
+ placeholder="选择开始时间"
|
|
|
+ style="width: 160px"
|
|
|
+ :disabled-hours="() => specialDisabledHours()"
|
|
|
+ :disabled-minutes="(h: number) => specialDisabledMinutes(h)"
|
|
|
+ @visible-change="v => v && initSpecialPickerRange(item.name, 'start')"
|
|
|
+ @change="() => onSpecialTimeChange(item.name, 'start')"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="" label-width="10px">
|
|
|
+ <el-time-picker
|
|
|
+ v-model="item.endTime"
|
|
|
+ format="HH:mm"
|
|
|
+ value-format="HH:mm"
|
|
|
+ placeholder="选择结束时间"
|
|
|
+ style="width: 160px"
|
|
|
+ :disabled-hours="() => specialDisabledHours()"
|
|
|
+ :disabled-minutes="(h: number) => specialDisabledMinutes(h)"
|
|
|
+ @visible-change="v => v && initSpecialPickerRange(item.name, 'end')"
|
|
|
+ @change="() => onSpecialTimeChange(item.name, 'end')"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</template>
|
|
|
</el-form>
|
|
|
</div>
|
|
|
@@ -252,7 +254,9 @@ const form = reactive({
|
|
|
startTime: string;
|
|
|
endTime: string;
|
|
|
id?: number;
|
|
|
- essentialId?: number;
|
|
|
+ essentialId?: number | string;
|
|
|
+ /** 与商家端:用户在选择节日中勾选的行才展示时间配置;接口回显匹配上的也为 true */
|
|
|
+ userPickedSpecial?: boolean;
|
|
|
}[]
|
|
|
});
|
|
|
|
|
|
@@ -312,6 +316,57 @@ const allowedFestivalSet = computed(() => {
|
|
|
return set;
|
|
|
});
|
|
|
|
|
|
+/** 门店单条 businessType=2:均为 00:00 为全天,否则非全天;元旦固定非全天(与商家端 infoManagement) */
|
|
|
+function allDayFromStoreSpecialRow(storeRow: any): "allDay" | "notAllDay" {
|
|
|
+ if (!storeRow || Number(storeRow.businessType) !== 2) return "notAllDay";
|
|
|
+ const nm = String(
|
|
|
+ (storeRow.holidayInfo && storeRow.holidayInfo.festivalName) || storeRow.businessDate || storeRow.holidayType || ""
|
|
|
+ ).trim();
|
|
|
+ if (nm === "元旦") return "notAllDay";
|
|
|
+ const st = String(storeRow.startTime ?? "").trim();
|
|
|
+ const et = String(storeRow.endTime ?? "").trim();
|
|
|
+ return st === "00:00" && et === "00:00" ? "allDay" : "notAllDay";
|
|
|
+}
|
|
|
+
|
|
|
+function allDayFromStoreByHolidayName(holidayName: string): "allDay" | "notAllDay" {
|
|
|
+ const n = String(holidayName || "").trim();
|
|
|
+ if (!n) return "allDay";
|
|
|
+ const list = Array.isArray(listFromStoreInfo.value) ? listFromStoreInfo.value : [];
|
|
|
+ const row = list.find((item: any) => {
|
|
|
+ if (Number(item.businessType) !== 2) return false;
|
|
|
+ const fn = (item.holidayInfo && item.holidayInfo.festivalName != null ? String(item.holidayInfo.festivalName) : "").trim();
|
|
|
+ const bd = item.businessDate != null ? String(item.businessDate).trim() : "";
|
|
|
+ const ht = item.holidayType != null ? String(item.holidayType).trim() : "";
|
|
|
+ return fn === n || bd === n || ht === n;
|
|
|
+ });
|
|
|
+ return row ? allDayFromStoreSpecialRow(row) : "allDay";
|
|
|
+}
|
|
|
+
|
|
|
+function getStoreSpecialRowMeta(name: string): { id?: number; essentialId?: number | string } {
|
|
|
+ const n = String(name || "").trim();
|
|
|
+ const list = Array.isArray(listFromStoreInfo.value) ? listFromStoreInfo.value : [];
|
|
|
+ const row = list.find((item: any) => {
|
|
|
+ if (Number(item.businessType) !== 2) return false;
|
|
|
+ const fn = (item.holidayInfo && item.holidayInfo.festivalName != null ? String(item.holidayInfo.festivalName) : "").trim();
|
|
|
+ const bd = item.businessDate != null ? String(item.businessDate).trim() : "";
|
|
|
+ const ht = item.holidayType != null ? String(item.holidayType).trim() : "";
|
|
|
+ return fn === n || bd === n || ht === n;
|
|
|
+ });
|
|
|
+ if (!row) return {};
|
|
|
+ return { id: row.id != null ? Number(row.id) : undefined, essentialId: row.essentialId };
|
|
|
+}
|
|
|
+
|
|
|
+function rowHasSpecialTimeConfigured(s: (typeof form.specialList)[0]): boolean {
|
|
|
+ const st = String(s.startTime ?? "").trim();
|
|
|
+ const et = String(s.endTime ?? "").trim();
|
|
|
+ if (s.allDay === "allDay" && st === "00:00" && et === "00:00") return true;
|
|
|
+ return st !== "" && et !== "";
|
|
|
+}
|
|
|
+
|
|
|
+function showSpecialRowTimeUI(s: (typeof form.specialList)[0]): boolean {
|
|
|
+ return rowHasSpecialTimeConfigured(s) || s.userPickedSpecial === true;
|
|
|
+}
|
|
|
+
|
|
|
const selectedHolidayNames = computed({
|
|
|
get: () => form.specialList.map(s => s.name),
|
|
|
set: (val: string[]) => {
|
|
|
@@ -319,7 +374,23 @@ const selectedHolidayNames = computed({
|
|
|
form.specialList.forEach(s => {
|
|
|
existingMap[s.name] = s;
|
|
|
});
|
|
|
- form.specialList = val.map(name => existingMap[name] || { name, allDay: "allDay", startTime: "", endTime: "" });
|
|
|
+ form.specialList = val.map(name => {
|
|
|
+ const ex = existingMap[name];
|
|
|
+ const ad = allDayFromStoreByHolidayName(name);
|
|
|
+ const meta = getStoreSpecialRowMeta(name);
|
|
|
+ if (ex) {
|
|
|
+ return { ...ex, allDay: ad, userPickedSpecial: true };
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ name,
|
|
|
+ allDay: ad,
|
|
|
+ startTime: "",
|
|
|
+ endTime: "",
|
|
|
+ userPickedSpecial: true,
|
|
|
+ id: meta.id,
|
|
|
+ essentialId: meta.essentialId
|
|
|
+ };
|
|
|
+ });
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -573,20 +644,8 @@ async function fetchStoreInfoBusinessHours() {
|
|
|
const list = Array.isArray(res?.data) ? res.data : [];
|
|
|
listFromStoreInfo.value = list;
|
|
|
const normal = list.find((item: any) => item.businessType === 1);
|
|
|
- const specialItems = list.filter((item: any) => item.businessType === 2);
|
|
|
- if (specialItems.length && !form.specialList.length) {
|
|
|
- form.specialList = specialItems.map((s: any, index: number) => {
|
|
|
- const name = (s.holidayInfo && s.holidayInfo.festivalName) || s.businessDate || s.holidayType || `特殊营业${index + 1}`;
|
|
|
- return {
|
|
|
- name,
|
|
|
- allDay: "allDay" as const,
|
|
|
- startTime: "",
|
|
|
- endTime: "",
|
|
|
- id: s.id,
|
|
|
- essentialId: s.essentialId
|
|
|
- };
|
|
|
- });
|
|
|
- }
|
|
|
+ /** 与商家端:特殊营业由 getBookingBusinessHours + applyListBookingToForm 回填,此处不占位 */
|
|
|
+ form.specialList = [];
|
|
|
if (normal && !form.normalBook.startTime && !form.normalBook.endTime) {
|
|
|
form.normalBook.startTime = normal.startTime || "";
|
|
|
form.normalBook.endTime = normal.endTime || "23:59";
|
|
|
@@ -614,42 +673,64 @@ function applyListBookingToForm() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ const storeInfoList = Array.isArray(listFromStoreInfo.value) ? listFromStoreInfo.value : [];
|
|
|
+ const specialFromStore = storeInfoList.filter((item: any) => item.businessType === 2);
|
|
|
const specialFromBooking = list.filter((item: any) => item.businessType === 2);
|
|
|
- const specialFromStore = listFromStoreInfo.value.filter((item: any) => item.businessType === 2);
|
|
|
- if (specialFromStore.length && form.specialList.length) {
|
|
|
- form.specialList = specialFromStore.map((s: any, index: number) => {
|
|
|
- const name = (s.holidayInfo && s.holidayInfo.festivalName) || s.businessDate || s.holidayType || `特殊营业${index + 1}`;
|
|
|
- const match = specialFromBooking.find(
|
|
|
- (b: any) =>
|
|
|
- String(b.id) === String(s.id) ||
|
|
|
- String(b.essentialId) === String(s.essentialId) ||
|
|
|
- (b.holidayType != null && String(b.holidayType).trim() === String(name).trim())
|
|
|
- );
|
|
|
- if (!match) {
|
|
|
- return {
|
|
|
- name,
|
|
|
- allDay: "allDay" as const,
|
|
|
- startTime: "",
|
|
|
- endTime: "",
|
|
|
- id: s.id,
|
|
|
- essentialId: s.essentialId
|
|
|
- };
|
|
|
- }
|
|
|
- const startTime = match.startTime || "";
|
|
|
- const endTimeRaw = match.endTime || "23:59";
|
|
|
- /** 与 merchant 一致:特殊营业结束时间回显不扣「结束前不可预订」 */
|
|
|
- const endTime = endTimeRaw;
|
|
|
- const isAllDay = match.bookingTimeType === 1 || (startTime === "00:00" && endTimeRaw === "00:00");
|
|
|
+ const bookingEmpty = !list.length;
|
|
|
+ const useStoreInfoForSpecial = !list.length || !specialFromBooking.length;
|
|
|
+
|
|
|
+ /** 预约接口返回的 holidayType 集合;仅在此集合内的门店节日才回显预订时间(与商家端 applyListBookingToForm) */
|
|
|
+ const bookingHolidayTypeSet = new Set(
|
|
|
+ specialFromBooking.map((b: any) => (b.holidayType != null ? String(b.holidayType).trim() : "")).filter(Boolean)
|
|
|
+ );
|
|
|
+
|
|
|
+ if (useStoreInfoForSpecial) {
|
|
|
+ if (bookingEmpty) {
|
|
|
+ form.specialList = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ /** 有预约数据但未配置特殊营业行:不占位,用户先选节日 */
|
|
|
+ form.specialList = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 仅 getBookingBusinessHours 中 holidayType 与门店节日匹配时回显时间;allDay 以门店营业时间为准 */
|
|
|
+ form.specialList = specialFromStore.map((s: any, index: number) => {
|
|
|
+ const name = (s.holidayInfo && s.holidayInfo.festivalName) || s.businessDate || s.holidayType || `特殊营业${index + 1}`;
|
|
|
+ const storeFestivalName = (
|
|
|
+ s.holidayInfo && s.holidayInfo.festivalName != null ? String(s.holidayInfo.festivalName) : ""
|
|
|
+ ).trim();
|
|
|
+ const shouldEchoTime = bookingHolidayTypeSet.has(storeFestivalName);
|
|
|
+ const match = specialFromBooking.find(
|
|
|
+ (b: any) =>
|
|
|
+ String(b.id) === String(s.id) ||
|
|
|
+ String(b.essentialId) === String(s.essentialId) ||
|
|
|
+ (storeFestivalName !== "" && (b.holidayType != null ? String(b.holidayType) : "").trim() === storeFestivalName)
|
|
|
+ );
|
|
|
+ if (!match || !shouldEchoTime) {
|
|
|
return {
|
|
|
name,
|
|
|
- allDay: isAllDay ? "allDay" : "notAllDay",
|
|
|
- startTime,
|
|
|
- endTime,
|
|
|
+ allDay: allDayFromStoreSpecialRow(s),
|
|
|
+ startTime: "",
|
|
|
+ endTime: "",
|
|
|
id: s.id,
|
|
|
- essentialId: s.essentialId
|
|
|
+ essentialId: s.essentialId,
|
|
|
+ userPickedSpecial: false
|
|
|
};
|
|
|
- });
|
|
|
- }
|
|
|
+ }
|
|
|
+ const startTime = match.startTime || "";
|
|
|
+ const endTimeRaw = match.endTime || "23:59";
|
|
|
+ const endTime = endTimeRaw;
|
|
|
+ return {
|
|
|
+ name,
|
|
|
+ allDay: allDayFromStoreSpecialRow(s),
|
|
|
+ startTime,
|
|
|
+ endTime,
|
|
|
+ id: s.id,
|
|
|
+ essentialId: s.essentialId,
|
|
|
+ userPickedSpecial: true
|
|
|
+ };
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
async function fetchBookingBusinessHours() {
|
|
|
@@ -746,6 +827,24 @@ async function onSave() {
|
|
|
saveLoading.value = true;
|
|
|
try {
|
|
|
const businessDate = listFromStoreInfo.value.find((item: any) => item.businessType === 1)?.businessDate;
|
|
|
+ const specialListBuilt: any[] = [];
|
|
|
+ let sort = 0;
|
|
|
+ for (const cur of form.specialList) {
|
|
|
+ if (!showSpecialRowTimeUI(cur)) continue;
|
|
|
+ const isAllDay = cur.allDay === "allDay";
|
|
|
+ const st = (cur.startTime || "").trim();
|
|
|
+ const et = (cur.endTime || "").trim();
|
|
|
+ if (!isAllDay && !st && !et) continue;
|
|
|
+ specialListBuilt.push({
|
|
|
+ bookingTimeType: isAllDay ? 1 : 0,
|
|
|
+ businessType: 2,
|
|
|
+ holidayType: cur.name || "",
|
|
|
+ startTime: cur.startTime || "00:00",
|
|
|
+ endTime: cur.endTime || "00:00",
|
|
|
+ sort: sort++,
|
|
|
+ essentialId: cur.essentialId
|
|
|
+ });
|
|
|
+ }
|
|
|
const params: Record<string, any> = {
|
|
|
storeId: Number(storeId),
|
|
|
retainPositionFlag: form.base.keepPosition === "keep" ? 1 : 0,
|
|
|
@@ -762,17 +861,11 @@ async function onSave() {
|
|
|
bookingTimeType: form.normalBook.timeType === "allDay" ? 1 : 0,
|
|
|
startTime: form.normalBook.startTime || "",
|
|
|
endTime: form.normalBook.endTime || ""
|
|
|
- },
|
|
|
- specialBusinessHoursList: form.specialList.map((cur, i) => ({
|
|
|
- bookingTimeType: cur.allDay === "allDay" ? 1 : 0,
|
|
|
- businessType: 2,
|
|
|
- holidayType: cur.name || "",
|
|
|
- startTime: cur.startTime || "",
|
|
|
- endTime: cur.endTime || "",
|
|
|
- sort: i,
|
|
|
- essentialId: cur.essentialId
|
|
|
- }))
|
|
|
+ }
|
|
|
};
|
|
|
+ if (specialListBuilt.length > 0) {
|
|
|
+ params.specialBusinessHoursList = specialListBuilt;
|
|
|
+ }
|
|
|
await bookingSettingsSave(params);
|
|
|
ElMessage.success("保存成功");
|
|
|
} catch (e: any) {
|
|
|
@@ -829,6 +922,7 @@ onMounted(async () => {
|
|
|
margin-bottom: 18px;
|
|
|
}
|
|
|
:deep(.el-form-item__label) {
|
|
|
+ align-items: center;
|
|
|
font-weight: 400;
|
|
|
line-height: 20px;
|
|
|
color: #606266;
|