fix: 对齐营业时间抽屉与日期时间选择交互
This commit is contained in:
@@ -8,7 +8,7 @@ import type {
|
||||
|
||||
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 {
|
||||
addSlotForm: AddSlotFormState;
|
||||
@@ -36,18 +36,35 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
function getInputValue(event: Event) {
|
||||
const target = event.target as HTMLInputElement | null;
|
||||
const target = event.target as HTMLInputElement | HTMLTextAreaElement | null;
|
||||
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>
|
||||
|
||||
<template>
|
||||
<Drawer
|
||||
class="add-slot-drawer-wrap"
|
||||
:open="props.open"
|
||||
title="添加时段"
|
||||
:width="500"
|
||||
:mask-closable="false"
|
||||
:body-style="{ paddingBottom: '88px' }"
|
||||
:width="480"
|
||||
:mask-closable="true"
|
||||
@update:open="(value) => emit('update:open', value)"
|
||||
>
|
||||
<div class="form-block">
|
||||
@@ -111,20 +128,26 @@ function getInputValue(event: Event) {
|
||||
<div class="time-grid">
|
||||
<div class="form-block">
|
||||
<label class="form-label required">开始时间</label>
|
||||
<input
|
||||
<TimePicker
|
||||
:value="props.addSlotForm.startTime"
|
||||
type="time"
|
||||
class="native-input"
|
||||
@input="(event) => props.onSetStartTime(getInputValue(event))"
|
||||
value-format="HH:mm"
|
||||
format="HH:mm"
|
||||
:allow-clear="false"
|
||||
class="native-picker"
|
||||
input-read-only
|
||||
@update:value="(value) => props.onSetStartTime(readTimeValue(value))"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-block">
|
||||
<label class="form-label required">结束时间</label>
|
||||
<input
|
||||
<TimePicker
|
||||
:value="props.addSlotForm.endTime"
|
||||
type="time"
|
||||
class="native-input"
|
||||
@input="(event) => props.onSetEndTime(getInputValue(event))"
|
||||
value-format="HH:mm"
|
||||
format="HH:mm"
|
||||
:allow-clear="false"
|
||||
class="native-picker"
|
||||
input-read-only
|
||||
@update:value="(value) => props.onSetEndTime(readTimeValue(value))"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -150,16 +173,18 @@ function getInputValue(event: Event) {
|
||||
/>
|
||||
<span>单/小时</span>
|
||||
</div>
|
||||
<div class="capacity-hint">该时段内每小时最大接单量</div>
|
||||
</div>
|
||||
|
||||
<div class="form-block">
|
||||
<label class="form-label">备注</label>
|
||||
<Textarea
|
||||
<textarea
|
||||
:value="props.addSlotForm.remark"
|
||||
:rows="2"
|
||||
rows="2"
|
||||
class="native-textarea"
|
||||
placeholder="可选,如:午市高峰时段"
|
||||
@update:value="props.onSetRemark"
|
||||
/>
|
||||
@input="(event) => props.onSetRemark(getInputValue(event))"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
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 {
|
||||
copyCandidates: StoreListItemDto[];
|
||||
@@ -23,36 +25,51 @@ const emit = defineEmits<{
|
||||
(event: 'update:open', value: boolean): void;
|
||||
}>();
|
||||
|
||||
function readChecked(event: { target?: { checked?: boolean } }) {
|
||||
return Boolean(event.target?.checked);
|
||||
const selectedStoreIdSet = computed(() => new Set(props.copyTargetStoreIds));
|
||||
|
||||
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>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
:open="props.open"
|
||||
title="复制到其他门店"
|
||||
:confirm-loading="props.isCopySubmitting"
|
||||
ok-text="确认复制"
|
||||
cancel-text="取消"
|
||||
@ok="emit('submit')"
|
||||
title="复制营业时间到其他门店"
|
||||
:width="650"
|
||||
:footer="null"
|
||||
:mask-closable="true"
|
||||
wrap-class-name="copy-store-modal-wrap"
|
||||
@update:open="(value) => emit('update:open', value)"
|
||||
>
|
||||
<div class="copy-modal-content">
|
||||
<Alert
|
||||
message="将覆盖目标门店的现有设置,请谨慎操作"
|
||||
type="warning"
|
||||
show-icon
|
||||
/>
|
||||
<div class="copy-modal-warning">
|
||||
<span class="copy-modal-warning-icon">!</span>
|
||||
<span>将覆盖目标门店的现有设置,请谨慎操作</span>
|
||||
</div>
|
||||
|
||||
<div class="copy-all-row">
|
||||
<Checkbox
|
||||
:checked="props.isCopyAllChecked"
|
||||
:indeterminate="props.isCopyIndeterminate"
|
||||
@change="(event) => emit('checkAll', readChecked(event))"
|
||||
<div class="copy-all-row" @click="toggleAll">
|
||||
<span
|
||||
class="copy-check"
|
||||
:class="{
|
||||
checked: props.isCopyAllChecked,
|
||||
indeterminate: props.isCopyIndeterminate && !props.isCopyAllChecked,
|
||||
}"
|
||||
>
|
||||
全选
|
||||
</Checkbox>
|
||||
<span class="copy-check-mark"></span>
|
||||
</span>
|
||||
<span>全选</span>
|
||||
</div>
|
||||
|
||||
<div class="copy-store-list">
|
||||
@@ -60,34 +77,31 @@ function readChecked(event: { target?: { checked?: boolean } }) {
|
||||
v-for="store in props.copyCandidates"
|
||||
:key="store.id"
|
||||
class="copy-store-item"
|
||||
@click="
|
||||
emit('toggleStore', {
|
||||
storeId: store.id,
|
||||
checked: !props.copyTargetStoreIds.includes(store.id),
|
||||
})
|
||||
"
|
||||
@click="toggleStore(store.id)"
|
||||
>
|
||||
<Checkbox
|
||||
:checked="props.copyTargetStoreIds.includes(store.id)"
|
||||
@click.stop
|
||||
@change="
|
||||
(event) =>
|
||||
emit('storeChange', {
|
||||
storeId: store.id,
|
||||
checked: readChecked(event),
|
||||
})
|
||||
"
|
||||
/>
|
||||
<span
|
||||
class="copy-check"
|
||||
:class="{ checked: isStoreChecked(store.id) }"
|
||||
>
|
||||
<span class="copy-check-mark"></span>
|
||||
</span>
|
||||
<div class="copy-store-info">
|
||||
<div class="copy-store-name">{{ store.name }}</div>
|
||||
<div class="copy-store-address">{{ store.address || '--' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="copy-source-tip">
|
||||
来源门店:{{ props.selectedStoreName || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="copy-modal-footer">
|
||||
<Button @click="emit('update:open', false)">取消</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
:loading="props.isCopySubmitting"
|
||||
:disabled="props.copyTargetStoreIds.length === 0"
|
||||
@click="emit('submit')"
|
||||
>
|
||||
确认复制
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { DayEditFormState, SlotTypeOption } from '../types';
|
||||
|
||||
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 {
|
||||
dayEditForm: DayEditFormState;
|
||||
@@ -32,23 +32,56 @@ function getInputValue(event: Event) {
|
||||
const target = event.target as HTMLInputElement | null;
|
||||
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>
|
||||
|
||||
<template>
|
||||
<Drawer
|
||||
class="day-edit-drawer-wrap"
|
||||
:open="props.open"
|
||||
:title="props.title"
|
||||
:width="560"
|
||||
:mask-closable="false"
|
||||
:body-style="{ paddingBottom: '88px' }"
|
||||
:width="520"
|
||||
:mask-closable="true"
|
||||
@update:open="(value) => emit('update:open', value)"
|
||||
>
|
||||
<div class="day-open-row">
|
||||
<Switch
|
||||
:checked="props.dayEditForm.isOpen"
|
||||
@update:checked="(checked) => props.onSetDayOpen(Boolean(checked))"
|
||||
/>
|
||||
<span>今日营业</span>
|
||||
<label class="day-open-toggle">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="props.dayEditForm.isOpen"
|
||||
@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>
|
||||
@@ -64,52 +97,67 @@ function getInputValue(event: Event) {
|
||||
class="slot-edit-card"
|
||||
>
|
||||
<div class="slot-edit-head">
|
||||
<Select
|
||||
:value="slot.type"
|
||||
class="slot-type-select"
|
||||
:options="props.slotTypeOptions"
|
||||
@update:value="
|
||||
(value) => props.onSetSlotType(slot.id, Number(value) as SlotType)
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
danger
|
||||
size="small"
|
||||
<div class="slot-type-pill-group">
|
||||
<button
|
||||
v-for="item in props.slotTypeOptions"
|
||||
:key="item.value"
|
||||
type="button"
|
||||
class="slot-type-pill"
|
||||
:class="[
|
||||
getSlotTypePillClass(item.value),
|
||||
{ active: slot.type === item.value },
|
||||
]"
|
||||
@click="props.onSetSlotType(slot.id, item.value)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="slot-remove-btn"
|
||||
@click="emit('removeSlot', slot.id)"
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="time-grid">
|
||||
<div class="form-block">
|
||||
<label class="form-label required">开始</label>
|
||||
<input
|
||||
<label class="form-sub-label">开始</label>
|
||||
<TimePicker
|
||||
:value="slot.startTime"
|
||||
type="time"
|
||||
class="native-input"
|
||||
@input="
|
||||
(event) =>
|
||||
props.onSetSlotStartTime(slot.id, getInputValue(event))
|
||||
value-format="HH:mm"
|
||||
format="HH:mm"
|
||||
:allow-clear="false"
|
||||
class="native-picker"
|
||||
input-read-only
|
||||
@update:value="
|
||||
(value) =>
|
||||
props.onSetSlotStartTime(slot.id, readTimeValue(value))
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-block">
|
||||
<label class="form-label required">结束</label>
|
||||
<input
|
||||
<label class="form-sub-label">结束</label>
|
||||
<TimePicker
|
||||
:value="slot.endTime"
|
||||
type="time"
|
||||
class="native-input"
|
||||
@input="
|
||||
(event) => props.onSetSlotEndTime(slot.id, getInputValue(event))
|
||||
value-format="HH:mm"
|
||||
format="HH:mm"
|
||||
:allow-clear="false"
|
||||
class="native-picker"
|
||||
input-read-only
|
||||
@update:value="
|
||||
(value) => props.onSetSlotEndTime(slot.id, readTimeValue(value))
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="slot.type === props.slotTypeDelivery" class="form-block">
|
||||
<label class="form-label">容量</label>
|
||||
<div
|
||||
v-if="slot.type === props.slotTypeDelivery"
|
||||
class="form-block delivery-capacity-block"
|
||||
>
|
||||
<label class="capacity-title">配送上限</label>
|
||||
<div class="capacity-row">
|
||||
<input
|
||||
:value="slot.capacity"
|
||||
@@ -127,13 +175,14 @@ function getInputValue(event: Event) {
|
||||
}
|
||||
"
|
||||
/>
|
||||
<span>单/小时</span>
|
||||
<span class="capacity-unit">单/小时</span>
|
||||
</div>
|
||||
<div class="capacity-tip">每小时最大可接配送订单数</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="add-dashed-btn" @click="emit('addSlotRow')">
|
||||
添加时段
|
||||
+ 添加时段
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,7 +3,13 @@ import type { HolidayFormState } from '../types';
|
||||
|
||||
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 {
|
||||
holidayForm: HolidayFormState;
|
||||
@@ -32,18 +38,59 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
function getInputValue(event: Event) {
|
||||
const target = event.target as HTMLInputElement | null;
|
||||
const target = event.target as HTMLInputElement | HTMLTextAreaElement | null;
|
||||
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>
|
||||
|
||||
<template>
|
||||
<Drawer
|
||||
class="holiday-drawer-wrap"
|
||||
:open="props.open"
|
||||
:title="props.title"
|
||||
:width="500"
|
||||
:mask-closable="false"
|
||||
:body-style="{ paddingBottom: '88px' }"
|
||||
:width="480"
|
||||
:mask-closable="true"
|
||||
@update:open="(value) => emit('update:open', value)"
|
||||
>
|
||||
<div class="form-block">
|
||||
@@ -94,27 +141,26 @@ function getInputValue(event: Event) {
|
||||
</div>
|
||||
|
||||
<template v-if="props.holidayForm.dateMode === 'single'">
|
||||
<input
|
||||
<DatePicker
|
||||
:value="props.holidayForm.singleDate"
|
||||
type="date"
|
||||
class="native-input"
|
||||
@input="(event) => props.onSetSingleDate(getInputValue(event))"
|
||||
value-format="YYYY-MM-DD"
|
||||
format="YYYY-MM-DD"
|
||||
:allow-clear="false"
|
||||
class="native-picker"
|
||||
input-read-only
|
||||
@update:value="(value) => props.onSetSingleDate(readDateValue(value))"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="date-range-row">
|
||||
<input
|
||||
:value="props.holidayForm.rangeStart"
|
||||
type="date"
|
||||
class="native-input"
|
||||
@input="(event) => props.onSetRangeStart(getInputValue(event))"
|
||||
/>
|
||||
<span>~</span>
|
||||
<input
|
||||
:value="props.holidayForm.rangeEnd"
|
||||
type="date"
|
||||
class="native-input"
|
||||
@input="(event) => props.onSetRangeEnd(getInputValue(event))"
|
||||
<RangePicker
|
||||
:value="getRangePickerValue()"
|
||||
value-format="YYYY-MM-DD"
|
||||
format="YYYY-MM-DD"
|
||||
:allow-clear="false"
|
||||
class="native-picker native-range-picker"
|
||||
input-read-only
|
||||
@update:value="handleRangeChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -128,42 +174,54 @@ function getInputValue(event: Event) {
|
||||
<div class="time-grid">
|
||||
<div class="form-block">
|
||||
<label class="form-sub-label">开始</label>
|
||||
<input
|
||||
<TimePicker
|
||||
:value="props.holidayForm.startTime"
|
||||
type="time"
|
||||
class="native-input"
|
||||
@input="(event) => props.onSetStartTime(getInputValue(event))"
|
||||
value-format="HH:mm"
|
||||
format="HH:mm"
|
||||
:allow-clear="false"
|
||||
class="native-picker"
|
||||
input-read-only
|
||||
@update:value="
|
||||
(value) => props.onSetStartTime(readTimeValue(value))
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-block">
|
||||
<label class="form-sub-label">结束</label>
|
||||
<input
|
||||
<TimePicker
|
||||
:value="props.holidayForm.endTime"
|
||||
type="time"
|
||||
class="native-input"
|
||||
@input="(event) => props.onSetEndTime(getInputValue(event))"
|
||||
value-format="HH:mm"
|
||||
format="HH:mm"
|
||||
:allow-clear="false"
|
||||
class="native-picker"
|
||||
input-read-only
|
||||
@update:value="(value) => props.onSetEndTime(readTimeValue(value))"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-hint">特殊营业日的营业时间段</div>
|
||||
</div>
|
||||
|
||||
<div class="form-block">
|
||||
<label class="form-label required">原因</label>
|
||||
<Input
|
||||
<input
|
||||
:value="props.holidayForm.reason"
|
||||
type="text"
|
||||
class="native-input"
|
||||
placeholder="如:春节假期、情人节延长营业"
|
||||
@update:value="props.onSetReason"
|
||||
@input="(event) => props.onSetReason(getInputValue(event))"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-block">
|
||||
<label class="form-label">备注</label>
|
||||
<Textarea
|
||||
<textarea
|
||||
:value="props.holidayForm.remark"
|
||||
:rows="2"
|
||||
rows="2"
|
||||
class="native-textarea"
|
||||
placeholder="可选"
|
||||
@update:value="props.onSetRemark"
|
||||
/>
|
||||
@input="(event) => props.onSetRemark(getInputValue(event))"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
|
||||
@@ -232,29 +232,33 @@ const {
|
||||
</td>
|
||||
<td>{{ formatHolidayTime(holiday) }}</td>
|
||||
<td>{{ holiday.reason || '--' }}</td>
|
||||
<td>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
@click="openHolidayDrawer('edit', holiday)"
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确认删除该日期配置吗?"
|
||||
ok-text="确认"
|
||||
cancel-text="取消"
|
||||
@confirm="handleDeleteHoliday(holiday.id)"
|
||||
>
|
||||
<td class="holiday-op-cell">
|
||||
<div class="holiday-actions">
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
:loading="deletingHolidayId === holiday.id"
|
||||
class="holiday-action-btn"
|
||||
@click="openHolidayDrawer('edit', holiday)"
|
||||
>
|
||||
删除
|
||||
编辑
|
||||
</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>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -1,58 +1,185 @@
|
||||
/* 复制到门店弹窗区域样式。 */
|
||||
.page-hours {
|
||||
.copy-modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
/* 复制到其他门店弹窗:样式挂在 Modal wrap class,避免 Teleport 后样式丢失。 */
|
||||
.copy-store-modal-wrap {
|
||||
.ant-modal {
|
||||
width: 650px !important;
|
||||
max-width: calc(100vw - 32px);
|
||||
}
|
||||
|
||||
.copy-all-row {
|
||||
padding-bottom: 8px;
|
||||
.ant-modal-content {
|
||||
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;
|
||||
}
|
||||
|
||||
.copy-store-list {
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #f0f0f0;
|
||||
.ant-modal-title {
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.ant-modal-close {
|
||||
top: 18px;
|
||||
right: 20px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
color: #8f959e;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.copy-store-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
padding: 10px 12px;
|
||||
cursor: pointer;
|
||||
.ant-modal-close:hover {
|
||||
color: #4e5969;
|
||||
background: #f2f3f5;
|
||||
}
|
||||
|
||||
.copy-store-item + .copy-store-item {
|
||||
border-top: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.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;
|
||||
.ant-modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.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 {
|
||||
.type-pill-group {
|
||||
display: flex;
|
||||
|
||||
@@ -31,7 +31,27 @@
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
||||
Reference in New Issue
Block a user