136 lines
3.9 KiB
Vue
136 lines
3.9 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* 文件职责:班次模板卡片。
|
||
* 1. 提供早班/晚班/全天模板时间编辑。
|
||
* 2. 透出保存与重置事件。
|
||
*/
|
||
import type { Dayjs } from 'dayjs';
|
||
|
||
import type { ShiftType, StoreShiftTemplatesDto } from '#/api/store-staff';
|
||
|
||
import { Button, Card, TimePicker } from 'ant-design-vue';
|
||
import dayjs from 'dayjs';
|
||
|
||
interface Props {
|
||
isSaving: boolean;
|
||
isTemplateConfigured: boolean;
|
||
onSetTemplateTime: (payload: {
|
||
field: 'endTime' | 'startTime';
|
||
shiftType: Exclude<ShiftType, 'off'>;
|
||
value: string;
|
||
}) => void;
|
||
templates: StoreShiftTemplatesDto;
|
||
}
|
||
|
||
const props = defineProps<Props>();
|
||
|
||
const emit = defineEmits<{
|
||
(event: 'reset'): void;
|
||
(event: 'save'): void;
|
||
}>();
|
||
|
||
const templateRows: Array<{
|
||
colorClass: string;
|
||
label: string;
|
||
shiftType: Exclude<ShiftType, 'off'>;
|
||
}> = [
|
||
{ shiftType: 'morning', label: '早班', colorClass: 'template-dot-morning' },
|
||
{ shiftType: 'evening', label: '晚班', colorClass: 'template-dot-evening' },
|
||
{ shiftType: 'full', label: '全天', colorClass: 'template-dot-full' },
|
||
];
|
||
|
||
/** 将 HH:mm 字符串转换为时间组件值。 */
|
||
function toPickerValue(time: string) {
|
||
if (!time) return undefined;
|
||
return dayjs(`2000-01-01 ${time}`);
|
||
}
|
||
|
||
/** 将时间组件值转换为 HH:mm 字符串。 */
|
||
function toTimeText(value: Dayjs | null | string | undefined) {
|
||
if (!value) return '';
|
||
if (typeof value === 'string') return value;
|
||
return value ? value.format('HH:mm') : '';
|
||
}
|
||
|
||
/** 处理模板时间变更。 */
|
||
function handleTemplateTimeChange(payload: {
|
||
field: 'endTime' | 'startTime';
|
||
shiftType: Exclude<ShiftType, 'off'>;
|
||
value: Dayjs | null | string;
|
||
}) {
|
||
props.onSetTemplateTime({
|
||
shiftType: payload.shiftType,
|
||
field: payload.field,
|
||
value: toTimeText(payload.value),
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<Card :bordered="false" class="staff-card">
|
||
<template #title>
|
||
<span class="section-title">班次模板</span>
|
||
</template>
|
||
|
||
<div v-if="!props.isTemplateConfigured" class="template-guide">
|
||
当前门店尚未配置班次模板,请先设置并保存模板,再进行员工排班。
|
||
</div>
|
||
|
||
<div class="template-list">
|
||
<div
|
||
v-for="row in templateRows"
|
||
:key="row.shiftType"
|
||
class="template-row"
|
||
>
|
||
<span class="template-dot" :class="row.colorClass"></span>
|
||
<span class="template-label">{{ row.label }}</span>
|
||
|
||
<div class="template-time-group">
|
||
<TimePicker
|
||
:value="toPickerValue(props.templates[row.shiftType].startTime)"
|
||
format="HH:mm"
|
||
:allow-clear="false"
|
||
@update:value="
|
||
(value) =>
|
||
handleTemplateTimeChange({
|
||
shiftType: row.shiftType,
|
||
field: 'startTime',
|
||
value,
|
||
})
|
||
"
|
||
/>
|
||
<span class="template-time-separator">~</span>
|
||
<TimePicker
|
||
:value="toPickerValue(props.templates[row.shiftType].endTime)"
|
||
format="HH:mm"
|
||
:allow-clear="false"
|
||
@update:value="
|
||
(value) =>
|
||
handleTemplateTimeChange({
|
||
shiftType: row.shiftType,
|
||
field: 'endTime',
|
||
value,
|
||
})
|
||
"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="template-tip">
|
||
{{
|
||
props.isTemplateConfigured
|
||
? '调整模板后,个人排班和周排班中的同类型班次会同步到新的时间段。'
|
||
: '模板保存成功后,员工列表中的“排班”和“编辑排班”会自动可用。'
|
||
}}
|
||
</div>
|
||
|
||
<div class="staff-card-actions">
|
||
<Button :disabled="props.isSaving" @click="emit('reset')">重置</Button>
|
||
<Button type="primary" :loading="props.isSaving" @click="emit('save')">
|
||
保存模板
|
||
</Button>
|
||
</div>
|
||
</Card>
|
||
</template>
|