fix: 对齐营业时间抽屉与日期时间选择交互
This commit is contained in:
@@ -8,7 +8,7 @@ import type {
|
|||||||
|
|
||||||
import type { SlotType } from '#/api/store-hours';
|
import type { SlotType } from '#/api/store-hours';
|
||||||
|
|
||||||
import { Button, Drawer, Textarea } from 'ant-design-vue';
|
import { Button, Drawer, TimePicker } from 'ant-design-vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
addSlotForm: AddSlotFormState;
|
addSlotForm: AddSlotFormState;
|
||||||
@@ -36,18 +36,35 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
function getInputValue(event: Event) {
|
function getInputValue(event: Event) {
|
||||||
const target = event.target as HTMLInputElement | null;
|
const target = event.target as HTMLInputElement | HTMLTextAreaElement | null;
|
||||||
return target?.value ?? '';
|
return target?.value ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasFormatMethod(
|
||||||
|
value: unknown,
|
||||||
|
): value is { format: (pattern: string) => string } {
|
||||||
|
return Boolean(
|
||||||
|
value &&
|
||||||
|
typeof value === 'object' &&
|
||||||
|
'format' in value &&
|
||||||
|
typeof (value as { format?: unknown }).format === 'function',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readTimeValue(value: unknown) {
|
||||||
|
if (typeof value === 'string') return value;
|
||||||
|
if (hasFormatMethod(value)) return value.format('HH:mm');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Drawer
|
<Drawer
|
||||||
|
class="add-slot-drawer-wrap"
|
||||||
:open="props.open"
|
:open="props.open"
|
||||||
title="添加时段"
|
title="添加时段"
|
||||||
:width="500"
|
:width="480"
|
||||||
:mask-closable="false"
|
:mask-closable="true"
|
||||||
:body-style="{ paddingBottom: '88px' }"
|
|
||||||
@update:open="(value) => emit('update:open', value)"
|
@update:open="(value) => emit('update:open', value)"
|
||||||
>
|
>
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
@@ -111,20 +128,26 @@ function getInputValue(event: Event) {
|
|||||||
<div class="time-grid">
|
<div class="time-grid">
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<label class="form-label required">开始时间</label>
|
<label class="form-label required">开始时间</label>
|
||||||
<input
|
<TimePicker
|
||||||
:value="props.addSlotForm.startTime"
|
:value="props.addSlotForm.startTime"
|
||||||
type="time"
|
value-format="HH:mm"
|
||||||
class="native-input"
|
format="HH:mm"
|
||||||
@input="(event) => props.onSetStartTime(getInputValue(event))"
|
:allow-clear="false"
|
||||||
|
class="native-picker"
|
||||||
|
input-read-only
|
||||||
|
@update:value="(value) => props.onSetStartTime(readTimeValue(value))"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<label class="form-label required">结束时间</label>
|
<label class="form-label required">结束时间</label>
|
||||||
<input
|
<TimePicker
|
||||||
:value="props.addSlotForm.endTime"
|
:value="props.addSlotForm.endTime"
|
||||||
type="time"
|
value-format="HH:mm"
|
||||||
class="native-input"
|
format="HH:mm"
|
||||||
@input="(event) => props.onSetEndTime(getInputValue(event))"
|
:allow-clear="false"
|
||||||
|
class="native-picker"
|
||||||
|
input-read-only
|
||||||
|
@update:value="(value) => props.onSetEndTime(readTimeValue(value))"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,16 +173,18 @@ function getInputValue(event: Event) {
|
|||||||
/>
|
/>
|
||||||
<span>单/小时</span>
|
<span>单/小时</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="capacity-hint">该时段内每小时最大接单量</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<label class="form-label">备注</label>
|
<label class="form-label">备注</label>
|
||||||
<Textarea
|
<textarea
|
||||||
:value="props.addSlotForm.remark"
|
:value="props.addSlotForm.remark"
|
||||||
:rows="2"
|
rows="2"
|
||||||
|
class="native-textarea"
|
||||||
placeholder="可选,如:午市高峰时段"
|
placeholder="可选,如:午市高峰时段"
|
||||||
@update:value="props.onSetRemark"
|
@input="(event) => props.onSetRemark(getInputValue(event))"
|
||||||
/>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { StoreListItemDto } from '#/api/store';
|
import type { StoreListItemDto } from '#/api/store';
|
||||||
|
|
||||||
import { Alert, Checkbox, Modal } from 'ant-design-vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import { Button, Modal } from 'ant-design-vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
copyCandidates: StoreListItemDto[];
|
copyCandidates: StoreListItemDto[];
|
||||||
@@ -23,36 +25,51 @@ const emit = defineEmits<{
|
|||||||
(event: 'update:open', value: boolean): void;
|
(event: 'update:open', value: boolean): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function readChecked(event: { target?: { checked?: boolean } }) {
|
const selectedStoreIdSet = computed(() => new Set(props.copyTargetStoreIds));
|
||||||
return Boolean(event.target?.checked);
|
|
||||||
|
function isStoreChecked(storeId: string) {
|
||||||
|
return selectedStoreIdSet.value.has(storeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleStore(storeId: string) {
|
||||||
|
emit('toggleStore', {
|
||||||
|
storeId,
|
||||||
|
checked: !isStoreChecked(storeId),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAll() {
|
||||||
|
emit('checkAll', !props.isCopyAllChecked);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<Modal
|
||||||
:open="props.open"
|
:open="props.open"
|
||||||
title="复制到其他门店"
|
title="复制营业时间到其他门店"
|
||||||
:confirm-loading="props.isCopySubmitting"
|
:width="650"
|
||||||
ok-text="确认复制"
|
:footer="null"
|
||||||
cancel-text="取消"
|
:mask-closable="true"
|
||||||
@ok="emit('submit')"
|
wrap-class-name="copy-store-modal-wrap"
|
||||||
@update:open="(value) => emit('update:open', value)"
|
@update:open="(value) => emit('update:open', value)"
|
||||||
>
|
>
|
||||||
<div class="copy-modal-content">
|
<div class="copy-modal-content">
|
||||||
<Alert
|
<div class="copy-modal-warning">
|
||||||
message="将覆盖目标门店的现有设置,请谨慎操作"
|
<span class="copy-modal-warning-icon">!</span>
|
||||||
type="warning"
|
<span>将覆盖目标门店的现有设置,请谨慎操作</span>
|
||||||
show-icon
|
</div>
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="copy-all-row">
|
<div class="copy-all-row" @click="toggleAll">
|
||||||
<Checkbox
|
<span
|
||||||
:checked="props.isCopyAllChecked"
|
class="copy-check"
|
||||||
:indeterminate="props.isCopyIndeterminate"
|
:class="{
|
||||||
@change="(event) => emit('checkAll', readChecked(event))"
|
checked: props.isCopyAllChecked,
|
||||||
|
indeterminate: props.isCopyIndeterminate && !props.isCopyAllChecked,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
全选
|
<span class="copy-check-mark"></span>
|
||||||
</Checkbox>
|
</span>
|
||||||
|
<span>全选</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="copy-store-list">
|
<div class="copy-store-list">
|
||||||
@@ -60,34 +77,31 @@ function readChecked(event: { target?: { checked?: boolean } }) {
|
|||||||
v-for="store in props.copyCandidates"
|
v-for="store in props.copyCandidates"
|
||||||
:key="store.id"
|
:key="store.id"
|
||||||
class="copy-store-item"
|
class="copy-store-item"
|
||||||
@click="
|
@click="toggleStore(store.id)"
|
||||||
emit('toggleStore', {
|
|
||||||
storeId: store.id,
|
|
||||||
checked: !props.copyTargetStoreIds.includes(store.id),
|
|
||||||
})
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<Checkbox
|
<span
|
||||||
:checked="props.copyTargetStoreIds.includes(store.id)"
|
class="copy-check"
|
||||||
@click.stop
|
:class="{ checked: isStoreChecked(store.id) }"
|
||||||
@change="
|
>
|
||||||
(event) =>
|
<span class="copy-check-mark"></span>
|
||||||
emit('storeChange', {
|
</span>
|
||||||
storeId: store.id,
|
|
||||||
checked: readChecked(event),
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<div class="copy-store-info">
|
<div class="copy-store-info">
|
||||||
<div class="copy-store-name">{{ store.name }}</div>
|
<div class="copy-store-name">{{ store.name }}</div>
|
||||||
<div class="copy-store-address">{{ store.address || '--' }}</div>
|
<div class="copy-store-address">{{ store.address || '--' }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="copy-source-tip">
|
<div class="copy-modal-footer">
|
||||||
来源门店:{{ props.selectedStoreName || '--' }}
|
<Button @click="emit('update:open', false)">取消</Button>
|
||||||
</div>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
:loading="props.isCopySubmitting"
|
||||||
|
:disabled="props.copyTargetStoreIds.length === 0"
|
||||||
|
@click="emit('submit')"
|
||||||
|
>
|
||||||
|
确认复制
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { DayEditFormState, SlotTypeOption } from '../types';
|
|||||||
|
|
||||||
import type { SlotType } from '#/api/store-hours';
|
import type { SlotType } from '#/api/store-hours';
|
||||||
|
|
||||||
import { Button, Drawer, Select, Switch } from 'ant-design-vue';
|
import { Button, Drawer, TimePicker } from 'ant-design-vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
dayEditForm: DayEditFormState;
|
dayEditForm: DayEditFormState;
|
||||||
@@ -32,23 +32,56 @@ function getInputValue(event: Event) {
|
|||||||
const target = event.target as HTMLInputElement | null;
|
const target = event.target as HTMLInputElement | null;
|
||||||
return target?.value ?? '';
|
return target?.value ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasFormatMethod(
|
||||||
|
value: unknown,
|
||||||
|
): value is { format: (pattern: string) => string } {
|
||||||
|
return Boolean(
|
||||||
|
value &&
|
||||||
|
typeof value === 'object' &&
|
||||||
|
'format' in value &&
|
||||||
|
typeof (value as { format?: unknown }).format === 'function',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readTimeValue(value: unknown) {
|
||||||
|
if (typeof value === 'string') return value;
|
||||||
|
if (hasFormatMethod(value)) return value.format('HH:mm');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCheckedValue(event: Event) {
|
||||||
|
const target = event.target as HTMLInputElement | null;
|
||||||
|
return Boolean(target?.checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSlotTypePillClass(type: SlotType) {
|
||||||
|
const value = Number(type);
|
||||||
|
if (value === 1) return 'tp-biz';
|
||||||
|
if (value === 2) return 'tp-del';
|
||||||
|
return 'tp-pick';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Drawer
|
<Drawer
|
||||||
|
class="day-edit-drawer-wrap"
|
||||||
:open="props.open"
|
:open="props.open"
|
||||||
:title="props.title"
|
:title="props.title"
|
||||||
:width="560"
|
:width="520"
|
||||||
:mask-closable="false"
|
:mask-closable="true"
|
||||||
:body-style="{ paddingBottom: '88px' }"
|
|
||||||
@update:open="(value) => emit('update:open', value)"
|
@update:open="(value) => emit('update:open', value)"
|
||||||
>
|
>
|
||||||
<div class="day-open-row">
|
<div class="day-open-row">
|
||||||
<Switch
|
<label class="day-open-toggle">
|
||||||
:checked="props.dayEditForm.isOpen"
|
<input
|
||||||
@update:checked="(checked) => props.onSetDayOpen(Boolean(checked))"
|
type="checkbox"
|
||||||
/>
|
:checked="props.dayEditForm.isOpen"
|
||||||
<span>今日营业</span>
|
@change="(event) => props.onSetDayOpen(getCheckedValue(event))"
|
||||||
|
/>
|
||||||
|
<span class="day-open-slider"></span>
|
||||||
|
</label>
|
||||||
|
<span class="day-open-text">今日营业</span>
|
||||||
<span v-if="!props.dayEditForm.isOpen" class="day-close-hint">
|
<span v-if="!props.dayEditForm.isOpen" class="day-close-hint">
|
||||||
该日休息,不接单
|
该日休息,不接单
|
||||||
</span>
|
</span>
|
||||||
@@ -64,52 +97,67 @@ function getInputValue(event: Event) {
|
|||||||
class="slot-edit-card"
|
class="slot-edit-card"
|
||||||
>
|
>
|
||||||
<div class="slot-edit-head">
|
<div class="slot-edit-head">
|
||||||
<Select
|
<div class="slot-type-pill-group">
|
||||||
:value="slot.type"
|
<button
|
||||||
class="slot-type-select"
|
v-for="item in props.slotTypeOptions"
|
||||||
:options="props.slotTypeOptions"
|
:key="item.value"
|
||||||
@update:value="
|
type="button"
|
||||||
(value) => props.onSetSlotType(slot.id, Number(value) as SlotType)
|
class="slot-type-pill"
|
||||||
"
|
:class="[
|
||||||
/>
|
getSlotTypePillClass(item.value),
|
||||||
<Button
|
{ active: slot.type === item.value },
|
||||||
type="text"
|
]"
|
||||||
danger
|
@click="props.onSetSlotType(slot.id, item.value)"
|
||||||
size="small"
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="slot-remove-btn"
|
||||||
@click="emit('removeSlot', slot.id)"
|
@click="emit('removeSlot', slot.id)"
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="time-grid">
|
<div class="time-grid">
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<label class="form-label required">开始</label>
|
<label class="form-sub-label">开始</label>
|
||||||
<input
|
<TimePicker
|
||||||
:value="slot.startTime"
|
:value="slot.startTime"
|
||||||
type="time"
|
value-format="HH:mm"
|
||||||
class="native-input"
|
format="HH:mm"
|
||||||
@input="
|
:allow-clear="false"
|
||||||
(event) =>
|
class="native-picker"
|
||||||
props.onSetSlotStartTime(slot.id, getInputValue(event))
|
input-read-only
|
||||||
|
@update:value="
|
||||||
|
(value) =>
|
||||||
|
props.onSetSlotStartTime(slot.id, readTimeValue(value))
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<label class="form-label required">结束</label>
|
<label class="form-sub-label">结束</label>
|
||||||
<input
|
<TimePicker
|
||||||
:value="slot.endTime"
|
:value="slot.endTime"
|
||||||
type="time"
|
value-format="HH:mm"
|
||||||
class="native-input"
|
format="HH:mm"
|
||||||
@input="
|
:allow-clear="false"
|
||||||
(event) => props.onSetSlotEndTime(slot.id, getInputValue(event))
|
class="native-picker"
|
||||||
|
input-read-only
|
||||||
|
@update:value="
|
||||||
|
(value) => props.onSetSlotEndTime(slot.id, readTimeValue(value))
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="slot.type === props.slotTypeDelivery" class="form-block">
|
<div
|
||||||
<label class="form-label">容量</label>
|
v-if="slot.type === props.slotTypeDelivery"
|
||||||
|
class="form-block delivery-capacity-block"
|
||||||
|
>
|
||||||
|
<label class="capacity-title">配送上限</label>
|
||||||
<div class="capacity-row">
|
<div class="capacity-row">
|
||||||
<input
|
<input
|
||||||
:value="slot.capacity"
|
:value="slot.capacity"
|
||||||
@@ -127,13 +175,14 @@ function getInputValue(event: Event) {
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<span>单/小时</span>
|
<span class="capacity-unit">单/小时</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="capacity-tip">每小时最大可接配送订单数</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="add-dashed-btn" @click="emit('addSlotRow')">
|
<button type="button" class="add-dashed-btn" @click="emit('addSlotRow')">
|
||||||
添加时段
|
+ 添加时段
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,13 @@ import type { HolidayFormState } from '../types';
|
|||||||
|
|
||||||
import type { HolidayType } from '#/api/store-hours';
|
import type { HolidayType } from '#/api/store-hours';
|
||||||
|
|
||||||
import { Button, Drawer, Input, Textarea } from 'ant-design-vue';
|
import {
|
||||||
|
Button,
|
||||||
|
DatePicker,
|
||||||
|
Drawer,
|
||||||
|
RangePicker,
|
||||||
|
TimePicker,
|
||||||
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
holidayForm: HolidayFormState;
|
holidayForm: HolidayFormState;
|
||||||
@@ -32,18 +38,59 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
function getInputValue(event: Event) {
|
function getInputValue(event: Event) {
|
||||||
const target = event.target as HTMLInputElement | null;
|
const target = event.target as HTMLInputElement | HTMLTextAreaElement | null;
|
||||||
return target?.value ?? '';
|
return target?.value ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasFormatMethod(
|
||||||
|
value: unknown,
|
||||||
|
): value is { format: (pattern: string) => string } {
|
||||||
|
return Boolean(
|
||||||
|
value &&
|
||||||
|
typeof value === 'object' &&
|
||||||
|
'format' in value &&
|
||||||
|
typeof (value as { format?: unknown }).format === 'function',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readDateValue(value: unknown) {
|
||||||
|
if (typeof value === 'string') return value;
|
||||||
|
if (hasFormatMethod(value)) return value.format('YYYY-MM-DD');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function readDateRangeValue(value: unknown): [string, string] {
|
||||||
|
if (!Array.isArray(value) || value.length < 2) return ['', ''];
|
||||||
|
return [readDateValue(value[0]), readDateValue(value[1])];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRangePickerValue(): [string, string] | undefined {
|
||||||
|
const { rangeStart, rangeEnd } = props.holidayForm;
|
||||||
|
if (!rangeStart || !rangeEnd) return undefined;
|
||||||
|
const rangeValue: [string, string] = [rangeStart, rangeEnd];
|
||||||
|
return rangeValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRangeChange(value: unknown) {
|
||||||
|
const [start, end] = readDateRangeValue(value);
|
||||||
|
props.onSetRangeStart(start);
|
||||||
|
props.onSetRangeEnd(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readTimeValue(value: unknown) {
|
||||||
|
if (typeof value === 'string') return value;
|
||||||
|
if (hasFormatMethod(value)) return value.format('HH:mm');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Drawer
|
<Drawer
|
||||||
|
class="holiday-drawer-wrap"
|
||||||
:open="props.open"
|
:open="props.open"
|
||||||
:title="props.title"
|
:title="props.title"
|
||||||
:width="500"
|
:width="480"
|
||||||
:mask-closable="false"
|
:mask-closable="true"
|
||||||
:body-style="{ paddingBottom: '88px' }"
|
|
||||||
@update:open="(value) => emit('update:open', value)"
|
@update:open="(value) => emit('update:open', value)"
|
||||||
>
|
>
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
@@ -94,27 +141,26 @@ function getInputValue(event: Event) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="props.holidayForm.dateMode === 'single'">
|
<template v-if="props.holidayForm.dateMode === 'single'">
|
||||||
<input
|
<DatePicker
|
||||||
:value="props.holidayForm.singleDate"
|
:value="props.holidayForm.singleDate"
|
||||||
type="date"
|
value-format="YYYY-MM-DD"
|
||||||
class="native-input"
|
format="YYYY-MM-DD"
|
||||||
@input="(event) => props.onSetSingleDate(getInputValue(event))"
|
:allow-clear="false"
|
||||||
|
class="native-picker"
|
||||||
|
input-read-only
|
||||||
|
@update:value="(value) => props.onSetSingleDate(readDateValue(value))"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="date-range-row">
|
<div class="date-range-row">
|
||||||
<input
|
<RangePicker
|
||||||
:value="props.holidayForm.rangeStart"
|
:value="getRangePickerValue()"
|
||||||
type="date"
|
value-format="YYYY-MM-DD"
|
||||||
class="native-input"
|
format="YYYY-MM-DD"
|
||||||
@input="(event) => props.onSetRangeStart(getInputValue(event))"
|
:allow-clear="false"
|
||||||
/>
|
class="native-picker native-range-picker"
|
||||||
<span>~</span>
|
input-read-only
|
||||||
<input
|
@update:value="handleRangeChange"
|
||||||
:value="props.holidayForm.rangeEnd"
|
|
||||||
type="date"
|
|
||||||
class="native-input"
|
|
||||||
@input="(event) => props.onSetRangeEnd(getInputValue(event))"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -128,42 +174,54 @@ function getInputValue(event: Event) {
|
|||||||
<div class="time-grid">
|
<div class="time-grid">
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<label class="form-sub-label">开始</label>
|
<label class="form-sub-label">开始</label>
|
||||||
<input
|
<TimePicker
|
||||||
:value="props.holidayForm.startTime"
|
:value="props.holidayForm.startTime"
|
||||||
type="time"
|
value-format="HH:mm"
|
||||||
class="native-input"
|
format="HH:mm"
|
||||||
@input="(event) => props.onSetStartTime(getInputValue(event))"
|
:allow-clear="false"
|
||||||
|
class="native-picker"
|
||||||
|
input-read-only
|
||||||
|
@update:value="
|
||||||
|
(value) => props.onSetStartTime(readTimeValue(value))
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<label class="form-sub-label">结束</label>
|
<label class="form-sub-label">结束</label>
|
||||||
<input
|
<TimePicker
|
||||||
:value="props.holidayForm.endTime"
|
:value="props.holidayForm.endTime"
|
||||||
type="time"
|
value-format="HH:mm"
|
||||||
class="native-input"
|
format="HH:mm"
|
||||||
@input="(event) => props.onSetEndTime(getInputValue(event))"
|
:allow-clear="false"
|
||||||
|
class="native-picker"
|
||||||
|
input-read-only
|
||||||
|
@update:value="(value) => props.onSetEndTime(readTimeValue(value))"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="time-hint">特殊营业日的营业时间段</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<label class="form-label required">原因</label>
|
<label class="form-label required">原因</label>
|
||||||
<Input
|
<input
|
||||||
:value="props.holidayForm.reason"
|
:value="props.holidayForm.reason"
|
||||||
|
type="text"
|
||||||
|
class="native-input"
|
||||||
placeholder="如:春节假期、情人节延长营业"
|
placeholder="如:春节假期、情人节延长营业"
|
||||||
@update:value="props.onSetReason"
|
@input="(event) => props.onSetReason(getInputValue(event))"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<label class="form-label">备注</label>
|
<label class="form-label">备注</label>
|
||||||
<Textarea
|
<textarea
|
||||||
:value="props.holidayForm.remark"
|
:value="props.holidayForm.remark"
|
||||||
:rows="2"
|
rows="2"
|
||||||
|
class="native-textarea"
|
||||||
placeholder="可选"
|
placeholder="可选"
|
||||||
@update:value="props.onSetRemark"
|
@input="(event) => props.onSetRemark(getInputValue(event))"
|
||||||
/>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
@@ -232,29 +232,33 @@ const {
|
|||||||
</td>
|
</td>
|
||||||
<td>{{ formatHolidayTime(holiday) }}</td>
|
<td>{{ formatHolidayTime(holiday) }}</td>
|
||||||
<td>{{ holiday.reason || '--' }}</td>
|
<td>{{ holiday.reason || '--' }}</td>
|
||||||
<td>
|
<td class="holiday-op-cell">
|
||||||
<Button
|
<div class="holiday-actions">
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
@click="openHolidayDrawer('edit', holiday)"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</Button>
|
|
||||||
<Popconfirm
|
|
||||||
title="确认删除该日期配置吗?"
|
|
||||||
ok-text="确认"
|
|
||||||
cancel-text="取消"
|
|
||||||
@confirm="handleDeleteHoliday(holiday.id)"
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
size="small"
|
||||||
danger
|
class="holiday-action-btn"
|
||||||
:loading="deletingHolidayId === holiday.id"
|
@click="openHolidayDrawer('edit', holiday)"
|
||||||
>
|
>
|
||||||
删除
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
<Popconfirm
|
||||||
|
title="确认删除该日期配置吗?"
|
||||||
|
ok-text="确认"
|
||||||
|
cancel-text="取消"
|
||||||
|
@confirm="handleDeleteHoliday(holiday.id)"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
danger
|
||||||
|
class="holiday-action-btn holiday-delete-btn"
|
||||||
|
:loading="deletingHolidayId === holiday.id"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -1,58 +1,185 @@
|
|||||||
/* 复制到门店弹窗区域样式。 */
|
/* 复制到其他门店弹窗:样式挂在 Modal wrap class,避免 Teleport 后样式丢失。 */
|
||||||
.page-hours {
|
.copy-store-modal-wrap {
|
||||||
.copy-modal-content {
|
.ant-modal {
|
||||||
display: flex;
|
width: 650px !important;
|
||||||
flex-direction: column;
|
max-width: calc(100vw - 32px);
|
||||||
gap: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-all-row {
|
.ant-modal-content {
|
||||||
padding-bottom: 8px;
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 14px;
|
||||||
|
box-shadow: 0 10px 30px rgb(0 0 0 / 12%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 22px 28px 16px;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-store-list {
|
.ant-modal-title {
|
||||||
max-height: 320px;
|
font-size: 17px;
|
||||||
overflow-y: auto;
|
font-weight: 700;
|
||||||
border: 1px solid #f0f0f0;
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal-close {
|
||||||
|
top: 18px;
|
||||||
|
right: 20px;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
color: #8f959e;
|
||||||
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-store-item {
|
.ant-modal-close:hover {
|
||||||
display: flex;
|
color: #4e5969;
|
||||||
gap: 10px;
|
background: #f2f3f5;
|
||||||
align-items: flex-start;
|
|
||||||
padding: 10px 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-store-item + .copy-store-item {
|
.ant-modal-body {
|
||||||
border-top: 1px solid #f5f5f5;
|
padding: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.copy-store-item:hover {
|
|
||||||
background: #fafcff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-store-info {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-store-name {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #1a1a2e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-store-address {
|
|
||||||
margin-top: 2px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #9ca3af;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-source-tip {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #6b7280;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.copy-modal-content {
|
||||||
|
padding: 18px 28px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-modal-warning {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
padding: 11px 14px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #c48b26;
|
||||||
|
background: #fffbe6;
|
||||||
|
border: 1px solid #f7e4a1;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-modal-warning-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
background: #f5b034;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-all-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 0 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-store-list {
|
||||||
|
max-height: 340px;
|
||||||
|
padding-top: 12px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-store-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 14px 14px 12px;
|
||||||
|
background: #fafbfc;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-store-item:hover {
|
||||||
|
background: #f3f6fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-store-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-store-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-store-address {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #86909c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-check {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
margin-top: 2px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #d9dde3;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-check-mark {
|
||||||
|
display: none;
|
||||||
|
width: 6px;
|
||||||
|
height: 10px;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
border-top: 0;
|
||||||
|
border-left: 0;
|
||||||
|
transform: rotate(45deg) translate(-1px, -1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-check.checked {
|
||||||
|
background: #1677ff;
|
||||||
|
border-color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-check.checked .copy-check-mark {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-check.indeterminate {
|
||||||
|
background: #1677ff;
|
||||||
|
border-color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-check.indeterminate .copy-check-mark {
|
||||||
|
display: block;
|
||||||
|
width: 10px;
|
||||||
|
height: 2px;
|
||||||
|
border: 0;
|
||||||
|
background: #fff;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-modal-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 14px 28px 18px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,650 @@
|
|||||||
/* 抽屉与编辑表单样式(新增时段、编辑时段、节假日抽屉)。 */
|
/* 抽屉与编辑表单样式(新增时段、编辑时段、节假日抽屉)。 */
|
||||||
|
.add-slot-drawer-wrap,
|
||||||
|
.day-edit-drawer-wrap,
|
||||||
|
.holiday-drawer-wrap {
|
||||||
|
.ant-drawer-content-wrapper {
|
||||||
|
box-shadow:
|
||||||
|
0 8px 24px rgb(0 0 0 / 8%),
|
||||||
|
0 2px 6px rgb(0 0 0 / 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-header {
|
||||||
|
min-height: 56px;
|
||||||
|
padding: 0 24px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f1f1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-close {
|
||||||
|
margin-inline-start: auto;
|
||||||
|
margin-inline-end: 0;
|
||||||
|
color: #999;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-close:hover {
|
||||||
|
color: #1f1f1f;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-body {
|
||||||
|
padding: 20px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-footer {
|
||||||
|
padding: 14px 24px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .drawer-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .drawer-footer .ant-btn {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .form-block {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .form-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1f1f1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .form-label.required::before {
|
||||||
|
margin-right: 3px;
|
||||||
|
color: #f5222d;
|
||||||
|
content: '*';
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .type-pill-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .type-pill {
|
||||||
|
padding: 6px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .type-pill.active.tp-biz {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #389e0d;
|
||||||
|
background: #f6ffed;
|
||||||
|
border-color: #b7eb8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .type-pill.active.tp-del {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1890ff;
|
||||||
|
background: #e6f7ff;
|
||||||
|
border-color: #91d5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .type-pill.active.tp-pick {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #d46b08;
|
||||||
|
background: #fff7e6;
|
||||||
|
border-color: #ffd591;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .day-pill-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .day-pill {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 42px;
|
||||||
|
height: 34px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #4b5563;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .day-pill.selected {
|
||||||
|
color: #fff;
|
||||||
|
background: #1677ff;
|
||||||
|
border-color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .quick-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .quick-btn {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #1677ff;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .quick-btn:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .time-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
column-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-picker,
|
||||||
|
.day-edit-drawer-wrap .native-picker,
|
||||||
|
.holiday-drawer-wrap .native-picker {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-picker.ant-picker,
|
||||||
|
.day-edit-drawer-wrap .native-picker.ant-picker,
|
||||||
|
.holiday-drawer-wrap .native-picker.ant-picker {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 34px;
|
||||||
|
padding: 0 11px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-picker.ant-picker:hover,
|
||||||
|
.day-edit-drawer-wrap .native-picker.ant-picker:hover,
|
||||||
|
.holiday-drawer-wrap .native-picker.ant-picker:hover {
|
||||||
|
border-color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-picker.ant-picker-focused,
|
||||||
|
.day-edit-drawer-wrap .native-picker.ant-picker-focused,
|
||||||
|
.holiday-drawer-wrap .native-picker.ant-picker-focused {
|
||||||
|
border-color: #1677ff;
|
||||||
|
box-shadow: 0 0 0 3px rgb(22 119 255 / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-picker .ant-picker-input > input,
|
||||||
|
.day-edit-drawer-wrap .native-picker .ant-picker-input > input,
|
||||||
|
.holiday-drawer-wrap .native-picker .ant-picker-input > input {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1f1f1f;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-picker .ant-picker-suffix,
|
||||||
|
.day-edit-drawer-wrap .native-picker .ant-picker-suffix,
|
||||||
|
.holiday-drawer-wrap .native-picker .ant-picker-suffix {
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 34px;
|
||||||
|
padding: 0 11px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1f1f1f;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-input:focus {
|
||||||
|
border-color: #1677ff;
|
||||||
|
box-shadow: 0 0 0 3px rgb(22 119 255 / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-textarea {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 11px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1f1f1f;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
outline: none;
|
||||||
|
resize: vertical;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .native-textarea:focus {
|
||||||
|
border-color: #1677ff;
|
||||||
|
box-shadow: 0 0 0 3px rgb(22 119 255 / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .capacity-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .capacity-input {
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-slot-drawer-wrap .capacity-hint {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .drawer-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .drawer-footer .ant-btn {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .day-open-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .day-open-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1f1f1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .day-open-toggle {
|
||||||
|
position: relative;
|
||||||
|
width: 40px;
|
||||||
|
height: 22px;
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .day-open-toggle input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .day-open-slider {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: #ccc;
|
||||||
|
border-radius: 11px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .day-open-slider::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
content: '';
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 1px 3px rgb(0 0 0 / 15%);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .day-open-toggle input:checked + .day-open-slider {
|
||||||
|
background: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap
|
||||||
|
.day-open-toggle
|
||||||
|
input:checked
|
||||||
|
+ .day-open-slider::before {
|
||||||
|
transform: translateX(18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .day-close-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-edit-list.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-edit-card {
|
||||||
|
padding: 12px 14px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
background: #f8f9fb;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-edit-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-type-pill-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-type-pill {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #4b5563;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-type-pill.active.tp-biz {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #389e0d;
|
||||||
|
background: #f6ffed;
|
||||||
|
border-color: #b7eb8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-type-pill.active.tp-del {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1890ff;
|
||||||
|
background: #e6f7ff;
|
||||||
|
border-color: #91d5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-type-pill.active.tp-pick {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #d46b08;
|
||||||
|
background: #fff7e6;
|
||||||
|
border-color: #ffd591;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-remove-btn {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ef4444;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .slot-remove-btn:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .time-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
column-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .form-block {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .form-sub-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .native-input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 34px;
|
||||||
|
padding: 0 11px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1f1f1f;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .native-input:focus {
|
||||||
|
border-color: #1677ff;
|
||||||
|
box-shadow: 0 0 0 3px rgb(22 119 255 / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .capacity-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .delivery-capacity-block {
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .capacity-title {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .capacity-input {
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .capacity-unit {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #475569;
|
||||||
|
background: #f1f5f9;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .capacity-tip {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .add-dashed-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 40px;
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ca3af;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px dashed #d1d5db;
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-edit-drawer-wrap .add-dashed-btn:hover {
|
||||||
|
color: #1677ff;
|
||||||
|
border-color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .drawer-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .drawer-footer .ant-btn {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .form-block {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .form-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1f1f1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .form-label.required::before {
|
||||||
|
margin-right: 3px;
|
||||||
|
color: #f5222d;
|
||||||
|
content: '*';
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .type-pill-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .type-pill {
|
||||||
|
padding: 6px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #4b5563;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .type-pill.active.ht-closed {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #ef4444;
|
||||||
|
background: #fff2f0;
|
||||||
|
border-color: #ffa39e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .type-pill.active.ht-special {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #f59e0b;
|
||||||
|
background: #fff7e6;
|
||||||
|
border-color: #ffd591;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .date-mode-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .date-mode-pill {
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #4b5563;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .date-mode-pill.active {
|
||||||
|
color: #fff;
|
||||||
|
background: #1677ff;
|
||||||
|
border-color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .date-range-row {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .native-range-picker.ant-picker {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .time-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
column-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .form-sub-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .native-input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 34px;
|
||||||
|
padding: 0 11px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1f1f1f;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .native-input:focus {
|
||||||
|
border-color: #1677ff;
|
||||||
|
box-shadow: 0 0 0 3px rgb(22 119 255 / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .native-textarea {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 11px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1f1f1f;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
outline: none;
|
||||||
|
resize: vertical;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .native-textarea:focus {
|
||||||
|
border-color: #1677ff;
|
||||||
|
box-shadow: 0 0 0 3px rgb(22 119 255 / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-drawer-wrap .time-hint {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
.page-hours {
|
.page-hours {
|
||||||
.type-pill-group {
|
.type-pill-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -31,7 +31,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.holiday-table .op-column {
|
.holiday-table .op-column {
|
||||||
width: 120px;
|
width: 140px;
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-op-cell {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-action-btn {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.holiday-delete-btn {
|
||||||
|
width: 56px;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.holiday-tag {
|
.holiday-tag {
|
||||||
|
|||||||
Reference in New Issue
Block a user