页面优化

This commit is contained in:
ahui
2025-08-13 10:29:52 +08:00
parent 2ea34bfbd5
commit 2a9f5be895
2 changed files with 383 additions and 384 deletions

View File

@@ -1,403 +1,402 @@
<template> <template>
<div style="width: 100%; height: 100%;"> <div style="width: 100%; height: 100%;">
<div class="selected-show" v-if="props.modelValue && props.selectable"> <div class="selected-show" v-if="props.modelValue && props.selectable">
<el-text>已选择:</el-text> <el-text>已选择:</el-text>
<el-tag v-if="props.multiple" v-for="item in data" closable @close="handleTagClose(item)"> <el-tag v-if="props.multiple" v-for="item in data" closable @close="handleTagClose(item)">
{{ item.toLocaleDateString('en-CA') }} {{ item.toLocaleDateString('en-CA') }}
</el-tag> </el-tag>
<el-tag v-else closable @close="handleTagClose(data)">{{ data?.toLocaleDateString('en-CA') }}</el-tag> <el-tag v-else closable @close="handleTagClose(data)">{{ data?.toLocaleDateString('en-CA') }}</el-tag>
<el-button v-if="props.modelValue" size="small" type="text" @click="clear">清空</el-button> <el-button v-if="props.modelValue" size="small" type="text" @click="clear">清空</el-button>
</div>
<div class="controls">
<div>
今天<el-text size="large">{{ today.toLocaleDateString('en-CA') }}</el-text>
</div> </div>
<div class="controls"> <!-- <div class="current-month">
<div>
今天<el-text size="large">{{ today.toLocaleDateString('en-CA') }}</el-text>
</div>
<!-- <div class="current-month">
<el-tag size="large" type="primary"> <el-tag size="large" type="primary">
{{ currentCalendarDate.getFullYear() }}{{ currentCalendarDate.getMonth() + 1 }} {{ currentCalendarDate.getFullYear() }}{{ currentCalendarDate.getMonth() + 1 }}
</el-tag> </el-tag>
</div> --> </div> -->
<div class="control-button" v-if="!(!!props.range && props.range[0] && props.range[1]) && props.showPageTurn"> <div class="control-button" v-if="!(!!props.range && props.range[0] && props.range[1]) && props.showPageTurn">
<el-button-group size="small" type="default" v-if="props.pageTurn"> <el-button-group size="small" type="default" v-if="props.pageTurn">
<el-popover trigger="click" width="160px"> <el-popover trigger="click" width="160px">
<template #reference> <template #reference>
<el-button type="text" size="small">节假日设置</el-button> <el-button type="text" size="small">节假日设置</el-button>
</template> </template>
<el-switch v-model="showHoliday" active-text="显示节日" inactive-text="关闭节日" inline-prompt /> <el-switch v-model="showHoliday" active-text="显示节日" inactive-text="关闭节日" inline-prompt />
<el-checkbox v-model="showLunarHoliday" label="农历节日" /> <el-checkbox v-model="showLunarHoliday" label="农历节日" />
<el-checkbox v-model="showJieQi" label="节气" /> <el-checkbox v-model="showJieQi" label="节气" />
<el-checkbox v-model="showDetailedHoliday" label="更多节日" /> <el-checkbox v-model="showDetailedHoliday" label="更多节日" />
</el-popover> </el-popover>
<el-button icon="DArrowLeft" @click="turnToPreY">上年</el-button> <el-button icon="DArrowLeft" @click="turnToPreY">上年</el-button>
<el-button icon="ArrowLeft" @click="turnToPreM">上月</el-button> <el-button icon="ArrowLeft" @click="turnToPreM">上月</el-button>
<el-button @click="turnToToday">今天</el-button> <el-button @click="turnToToday">今天</el-button>
<el-button icon="ArrowRight" @click="turnToNextM">下月</el-button> <el-button icon="ArrowRight" @click="turnToNextM">下月</el-button>
<el-button icon="DArrowRight" @click="turnToNextY">下年</el-button> <el-button icon="DArrowRight" @click="turnToNextY">下年</el-button>
</el-button-group> </el-button-group>
</div>
</div>
<el-divider style="margin: 4px;" />
<div class="calender">
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr>
<th class="calender-header" v-for="item, ind in ['日', '一', '二', '三', '四', '五', '六']" :key="ind">
{{ item }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="week in calendarList">
<td class="calender-td" v-for="item in week">
<div class="calender-cell" :data-date="item.date.toLocaleDateString('en-CA')" :class="{
'no-current-month': item.date.getMonth() !== currentCalendarDate.getMonth(),
'today': item.date.toDateString() === today.toDateString(),
'selected': item.selected,
'disabled': item.disabled,
}" @mouseenter="onCalenderCellHover" @mouseleave="onCalenderCellUnhover"
@click="(e: MouseEvent) => item.disabled ? null : onCalenderCellClick(e)">
<div class="calender-cell-header calender-cell-line">
<span>{{ item.date.getDate() }}</span>
<span v-if="item.selected">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 1024 1024">
<path fill="currentColor"
d="M77.248 415.04a64 64 0 0 1 90.496 0l226.304 226.304L846.528 188.8a64 64 0 1 1 90.56 90.496l-543.04 543.04-316.8-316.8a64 64 0 0 1 0-90.496z">
</path>
</svg>
</span>
</div>
<div class="calender-cell-body calender-cell-line">
<slot name="cell-body" v-bind="item">
</slot>
</div>
<div class="calender-cell-footer calender-cell-line">
<span>{{ item.holiday || '&nbsp;' }}</span>
<el-text v-if="item.date.toDateString() === today.toDateString()" type="danger">今天</el-text>
</div>
<!-- {{ item }} -->
</div>
</td>
</tr>
</tbody>
</table>
<div class="watermark" v-if="props.watermark" :style="watermarkPositionMap[props.watermarkPosition]">
{{ (currentCalendarDate.toLocaleDateString('en-CA').split('-').slice(0, 2)).join('-') }}
</div>
</div> </div>
</div> </div>
</template> <el-divider style="margin: 4px;" />
<div class="calender">
<script lang="ts" setup> <table style="width: 100%; border-collapse: collapse;">
import { useUi } from '@fast-crud/fast-crud'; <thead>
import { ref, defineProps, PropType, watch, computed, onMounted } from 'vue'; <tr>
import Holidays from 'date-holidays'; <th class="calender-header" v-for="item, ind in ['日', '一', '二', '三', '四', '五', '六']" :key="ind">
import Lunar from 'lunar-javascript'; {{ item }}
const LUNAR = Lunar.Lunar; // 农历 </th>
const SOLAR = Lunar.Solar; // 阳历 </tr>
</thead>
const props = defineProps({ <tbody>
modelValue: {}, <tr v-for="week in calendarList">
// 日期多选 <td class="calender-td" v-for="item in week">
multiple: { type: Boolean, default: false }, <div class="calender-cell" :data-date="item.date.toLocaleDateString('en-CA')" :class="{
// 日期范围 'no-current-month': item.date.getMonth() !== currentCalendarDate.getMonth(),
range: { type: Object as PropType<[Date, Date]> }, 'today': item.date.toDateString() === today.toDateString(),
// 可以翻页 'selected': item.selected,
pageTurn: { type: Boolean, default: true }, 'disabled': item.disabled,
// 跨页选择 }" @mouseenter="onCalenderCellHover" @mouseleave="onCalenderCellUnhover"
crossPage: { type: Boolean, default: false }, @click="(e: MouseEvent) => item.disabled ? null : onCalenderCellClick(e)">
// 显示年月水印和水印位置 <div class="calender-cell-header calender-cell-line">
watermark: { type: Boolean, default: true }, <span>{{ item.date.getDate() }}</span>
watermarkPosition: { type: Object as PropType<PositionType>, default: 'bottom-right' }, <span v-if="item.selected">
// 显示翻页控件 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 1024 1024">
showPageTurn: { type: Boolean, default: true }, <path fill="currentColor"
// 是否可选 d="M77.248 415.04a64 64 0 0 1 90.496 0l226.304 226.304L846.528 188.8a64 64 0 1 1 90.56 90.496l-543.04 543.04-316.8-316.8a64 64 0 0 1 0-90.496z">
selectable: { type: Boolean, default: true }, </path>
// 验证日期是否有效 </svg>
validDate: { type: Object as PropType<ValidDateFunc>, default: () => ((d: Date) => true) } </span>
}); </div>
type ValidDateFunc = (d: Date) => boolean; <div class="calender-cell-body calender-cell-line">
type PositionType = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center' | 'center-left' | 'center-right' | 'center-top'; <slot name="cell-body" v-bind="item">
const today = new Date(); </slot>
const showHoliday = ref<boolean>(true); // 显示节日 </div>
const showDetailedHoliday = ref<boolean>(false); // 显示详细的国际节日 <div class="calender-cell-footer calender-cell-line">
const showJieQi = ref<boolean>(true); // 显示节气 <span>{{ item.holiday || '&nbsp;' }}</span>
const showLunarHoliday = ref<boolean>(true); // 显示农历节日 <el-text v-if="item.date.toDateString() === today.toDateString()" type="danger">今天</el-text>
const watermarkPositionMap: { [key: string]: any } = { </div>
'top-left': { top: '40px', left: 0, transformOrigin: '0 0' }, <!-- {{ item }} -->
'top-right': { top: '40px', right: 0, transformOrigin: '100% 0' }, </div>
'bottom-left': { bottom: 0, left: 0, transformOrigin: '0 100%' }, </td>
'bottom-right': { bottom: 0, right: 0, transformOrigin: '100% 100%' }, </tr>
'center': { top: '50%', left: '50%', transformOrigin: '50% 50%', transform: 'translate(-50%, -50%) scale(10)' }, </tbody>
'center-left': { top: '50%', left: 0, transformOrigin: '0 50%' }, </table>
'center-right': { top: '50%', right: 0, transformOrigin: '100% 50%' }, <div class="watermark" v-if="props.watermark" :style="watermarkPositionMap[props.watermarkPosition]">
'center-top': { top: 0, left: '50%', transformOrigin: '50% 0', transform: 'translate(-50%, 40px) scale(10)' }, {{ (currentCalendarDate.toLocaleDateString('en-CA').split('-').slice(0, 2)).join('-') }}
'center-bottom': { bottom: 0, left: '50%', transformOrigin: '50% 100%', transform: 'translate(-50%, 0) scale(10)' }, </div>
}; </div>
// 获取当月第一周的第一天(包括上个月) </div>
const calendarFirstDay = (current: Date = new Date()) => { </template>
let today = new Date(current); // 指定天
let firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1); // 月初天 <script lang="ts" setup>
let weekOfFirstDay = firstDayOfMonth.getDay(); // 周几0日 import { useUi } from '@fast-crud/fast-crud';
if (weekOfFirstDay === 0) return new Date(firstDayOfMonth); // 是周日则直接返回 import { ref, defineProps, PropType, watch, computed, onMounted } from 'vue';
let firstDayOfWeek = new Date(firstDayOfMonth); import Holidays from 'date-holidays';
// 月初减去周几,不+1是因为从日历周日开始 import Lunar from 'lunar-javascript';
firstDayOfWeek.setDate(firstDayOfMonth.getDate() - weekOfFirstDay); const LUNAR = Lunar.Lunar; // 农历
return new Date(firstDayOfWeek); const SOLAR = Lunar.Solar; // 阳历
};
// 获取当月最后一周的最后一天(包括下个月) const props = defineProps({
const calendarLastDay = (current: Date = new Date()) => { modelValue: {},
let today = new Date(current); // 指定天 // 日期多选
let lastDayOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1); // 月末天 multiple: { type: Boolean, default: false },
lastDayOfMonth.setDate(lastDayOfMonth.getDate() - 1); // 日期范围
let weekOfFirstDay = lastDayOfMonth.getDay(); range: { type: Object as PropType<[Date, Date]> },
if (weekOfFirstDay === 6) return new Date(lastDayOfMonth); // 是周六则直接返回 // 可以翻页
let lastDayOfWeek = new Date(lastDayOfMonth); pageTurn: { type: Boolean, default: true },
// 月末加剩下周几,要-1是因为日历到周六结束 // 跨页选择
lastDayOfWeek.setDate(lastDayOfMonth.getDate() + (7 - weekOfFirstDay - 1)); crossPage: { type: Boolean, default: false },
return new Date(lastDayOfWeek); // 显示年月水印和水印位置
}; watermark: { type: Boolean, default: true },
const generateDateList = (startDate: Date, endDate: Date): Date[] => { // 生成日期列表 watermarkPosition: { type: Object as PropType<PositionType>, default: 'bottom-right' },
let dates = []; // 显示翻页控件
let s = new Date(startDate); showPageTurn: { type: Boolean, default: true },
let e = new Date(endDate); // 是否可选
while (s <= e) { selectable: { type: Boolean, default: true },
dates.push(new Date(s)); // 验证日期是否有效
s.setDate(s.getDate() + 1); validDate: { type: Object as PropType<ValidDateFunc>, default: () => ((d: Date) => true) }
} });
return dates; type ValidDateFunc = (d: Date) => boolean;
}; type PositionType = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center' | 'center-left' | 'center-right' | 'center-top';
// 日历当前页范围 const today = new Date();
interface CalendarCell { const showHoliday = ref<boolean>(true); // 显示节日
date: Date; const showDetailedHoliday = ref<boolean>(false); // 显示详细的国际节日
selected: boolean; const showJieQi = ref<boolean>(true); // 显示节气
disabled: boolean; const showLunarHoliday = ref<boolean>(true); // 显示农历节日
currentMonth: boolean; const watermarkPositionMap: { [key: string]: any } = {
holiday: string; 'top-left': { top: '40px', left: 0, transformOrigin: '0 0' },
}; 'top-right': { top: '40px', right: 0, transformOrigin: '100% 0' },
const currentCalendarDate = ref<Date>(new Date()); 'bottom-left': { bottom: 0, left: 0, transformOrigin: '0 100%' },
const calendarList = computed(() => { 'bottom-right': { bottom: 0, right: 0, transformOrigin: '100% 100%' },
let dates = (!!props.range && props.range[0] && props.range[1]) ? 'center': { top: '50%', left: '50%', transformOrigin: '50% 50%', transform: 'translate(-50%, -50%) scale(10)' },
generateDateList(props.range[0], props.range[1]) : 'center-left': { top: '50%', left: 0, transformOrigin: '0 50%' },
generateDateList(calendarFirstDay(currentCalendarDate.value), calendarLastDay(currentCalendarDate.value)); 'center-right': { top: '50%', right: 0, transformOrigin: '100% 50%' },
let proce_dates: CalendarCell[] = dates.map((value) => { 'center-top': { top: 0, left: '50%', transformOrigin: '50% 0', transform: 'translate(-50%, 40px) scale(10)' },
let solarDate = SOLAR.fromDate(value); 'center-bottom': { bottom: 0, left: '50%', transformOrigin: '50% 100%', transform: 'translate(-50%, 0) scale(10)' },
let lunarDate = solarDate.getLunar(); };
let solarHolidays: string[] = solarDate.getFestivals(); // 国历节日 // 获取当月第一周的第一天(包括上个月)
let lunarHolidays: string[] = lunarDate.getFestivals(); // 农历节日 const calendarFirstDay = (current: Date = new Date()) => {
let jieQi: string = lunarDate.getJieQi(); // 节气 let today = new Date(current); // 指定天
// 农历节日、国际节日、节气三选一 let firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1); // 月初天
let holiday = showHoliday.value ? ((showLunarHoliday.value ? lunarHolidays[0] : '') || (showJieQi.value ? jieQi : '') || let weekOfFirstDay = firstDayOfMonth.getDay(); // 周几0日
(showDetailedHoliday.value ? solarHolidays[0] : yearHolidays.value[value.toLocaleDateString('en-CA')])) : ''; // yearHolidays国际的 if (weekOfFirstDay === 0) return new Date(firstDayOfMonth); // 是周日则直接返回
return { let firstDayOfWeek = new Date(firstDayOfMonth);
date: value, // 月初减去周几,不+1是因为从日历周日开始
selected: props.multiple ? firstDayOfWeek.setDate(firstDayOfMonth.getDate() - weekOfFirstDay);
(data.value as Date[]).findIndex((v) => v.toLocaleDateString('en-CA') === value.toLocaleDateString('en-CA')) !== -1 : return new Date(firstDayOfWeek);
data.value?.toLocaleDateString('en-CA') === value.toLocaleDateString('en-CA'), };
disabled: !props.validDate(value), // 获取当月最后一周的最后一天(包括下个月)
currentMonth: value.getMonth() === currentCalendarDate.value.getMonth(), const calendarLastDay = (current: Date = new Date()) => {
// 农历节日、节气、法定日三选一 let today = new Date(current); // 指定天
holiday: holiday let lastDayOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1); // 月末天
} lastDayOfMonth.setDate(lastDayOfMonth.getDate() - 1);
}); let weekOfFirstDay = lastDayOfMonth.getDay();
let res: CalendarCell[][] = []; if (weekOfFirstDay === 6) return new Date(lastDayOfMonth); // 是周六则直接返回
for (let i = 0; i < 6; i++) res.push(proce_dates.slice(i * 7, (i + 1) * 7)); let lastDayOfWeek = new Date(lastDayOfMonth);
return res; // 月末加剩下周几,要-1是因为日历到周六结束
}); lastDayOfWeek.setDate(lastDayOfMonth.getDate() + (7 - weekOfFirstDay - 1));
// 控件 return new Date(lastDayOfWeek);
const turnToPreM = () => currentCalendarDate.value = new Date(currentCalendarDate.value.getFullYear(), currentCalendarDate.value.getMonth() - 1, 1); };
const turnToNextM = () => currentCalendarDate.value = new Date(currentCalendarDate.value.getFullYear(), currentCalendarDate.value.getMonth() + 1, 1); const generateDateList = (startDate: Date, endDate: Date): Date[] => { // 生成日期列表
const turnToPreY = () => currentCalendarDate.value = new Date(currentCalendarDate.value.getFullYear() - 1, currentCalendarDate.value.getMonth(), 1); let dates = [];
const turnToNextY = () => currentCalendarDate.value = new Date(currentCalendarDate.value.getFullYear() + 1, currentCalendarDate.value.getMonth(), 1); let s = new Date(startDate);
const turnToToday = () => currentCalendarDate.value = new Date(); let e = new Date(endDate);
// 如果禁止跨页则跨页时清空选择 while (s <= e) {
watch( dates.push(new Date(s));
() => currentCalendarDate.value, s.setDate(s.getDate() + 1);
(v, ov) => props.crossPage ? {} :
(v.toLocaleDateString('en-CA') === ov.toLocaleDateString('en-CA') ? {} : clear())
);
// 单元格事件
const onCalenderCellHover = ({ target }: MouseEvent) => (target as HTMLElement).classList.add('onhover');
const onCalenderCellUnhover = ({ target }: MouseEvent) => (target as HTMLElement).classList.remove('onhover');
const onCalenderCellClick = (e: MouseEvent) => {
if (!props.selectable) return;
let strValue = (e.target as HTMLElement).dataset.date as string;
if (strValue === undefined) return;
let value = new Date(strValue);
if (props.multiple) {
let d = (data.value as Date[]).map((v) => v.toLocaleDateString('en-CA'));
let ind = d.findIndex((v) => v === strValue);
if (ind === -1) d.push(strValue);
else d.splice(ind, 1);
onDataChange(d);
}
// 这里阻止了点击取消选中需要通过tag的x来取消
else (data.value?.toLocaleDateString('en-CA') === strValue ? {} : onDataChange(value));
};
// 选择回显
const handleTagClose = (d: Date) => {
let strValue = d.toLocaleDateString('en-CA');
if (props.multiple) {
let d = (data.value as Date[]).map((v) => v.toLocaleDateString('en-CA'));
d.splice(d.findIndex((v) => v === strValue), 1);
onDataChange(d);
}
else onDataChange(null);
};
// 节假日
const holidays = new Holidays('CN');
const yearHolidays = computed(() => {
let h = holidays.getHolidays(currentCalendarDate.value.getFullYear());
let proce_h: { [key: string]: string } = {};
let _h: string[] = [];
for (let i of h) {
let d = i.date.split(' ')[0];
let hn = i.name.split(' ')[0];
if (_h.includes(hn)) continue;
proce_h[d] = hn;
_h.push(hn);
}
return proce_h
});
// fs-crud部分
const data = ref<any>();
const emit = defineEmits(['update:modelValue', 'onSave', 'onClose', 'onClosed']);
watch(
() => props.modelValue,
(val) => {
if (val === undefined) data.value = props.multiple ? [] : null;
else data.value = props.multiple ? (val as Date[]).map((v: Date) => new Date(v)) : val;
},
{ immediate: true }
);
const { ui } = useUi();
const formValidator = ui.formItem.injectFormItemContext();
const onDataChange = (value: any) => {
emit('update:modelValue', value);
formValidator.onChange();
formValidator.onBlur();
};
const reset = () => { // 重置日历
currentCalendarDate.value = new Date();
onDataChange(props.multiple ? [] : null);
};
const clear = () => onDataChange(props.multiple ? [] : null); // 清空数据
defineExpose({
data,
onDataChange,
reset,
clear,
showHoliday,
showDetailedHoliday,
showJieQi,
showLunarHoliday
});
</script>
<style lang="scss" scoped>
.selected-show {
display: flex;
flex-wrap: wrap;
gap: 8px;
} }
return dates;
.controls { };
width: 100%; // 日历当前页范围
height: 40px; interface CalendarCell {
display: flex; date: Date;
align-items: center; selected: boolean;
justify-content: space-between; disabled: boolean;
currentMonth: boolean;
holiday: string;
};
const currentCalendarDate = ref<Date>(new Date());
const calendarList = computed(() => {
let dates = (!!props.range && props.range[0] && props.range[1]) ?
generateDateList(props.range[0], props.range[1]) :
generateDateList(calendarFirstDay(currentCalendarDate.value), calendarLastDay(currentCalendarDate.value));
let proce_dates: CalendarCell[] = dates.map((value) => {
let solarDate = SOLAR.fromDate(value);
let lunarDate = solarDate.getLunar();
let solarHolidays: string[] = solarDate.getFestivals(); // 国历节日
let lunarHolidays: string[] = lunarDate.getFestivals(); // 农历节日
let jieQi: string = lunarDate.getJieQi(); // 节气
// 农历节日、国际节日、节气三选一
let holiday = showHoliday.value ? ((showLunarHoliday.value ? lunarHolidays[0] : '') || (showJieQi.value ? jieQi : '') ||
(showDetailedHoliday.value ? solarHolidays[0] : yearHolidays.value[value.toLocaleDateString('en-CA')])) : ''; // yearHolidays国际的
return {
date: value,
selected: props.multiple ?
(data.value as Date[]).findIndex((v) => v.toLocaleDateString('en-CA') === value.toLocaleDateString('en-CA')) !== -1 :
data.value?.toLocaleDateString('en-CA') === value.toLocaleDateString('en-CA'),
disabled: !props.validDate(value),
currentMonth: value.getMonth() === currentCalendarDate.value.getMonth(),
// 农历节日、节气、法定日三选一
holiday: holiday
}
});
let res: CalendarCell[][] = [];
for (let i = 0; i < 6; i++) res.push(proce_dates.slice(i * 7, (i + 1) * 7));
return res;
});
// 控件
const turnToPreM = () => currentCalendarDate.value = new Date(currentCalendarDate.value.getFullYear(), currentCalendarDate.value.getMonth() - 1, 1);
const turnToNextM = () => currentCalendarDate.value = new Date(currentCalendarDate.value.getFullYear(), currentCalendarDate.value.getMonth() + 1, 1);
const turnToPreY = () => currentCalendarDate.value = new Date(currentCalendarDate.value.getFullYear() - 1, currentCalendarDate.value.getMonth(), 1);
const turnToNextY = () => currentCalendarDate.value = new Date(currentCalendarDate.value.getFullYear() + 1, currentCalendarDate.value.getMonth(), 1);
const turnToToday = () => currentCalendarDate.value = new Date();
// 如果禁止跨页则跨页时清空选择
watch(
() => currentCalendarDate.value,
(v, ov) => props.crossPage ? {} :
(v.toLocaleDateString('en-CA') === ov.toLocaleDateString('en-CA') ? {} : clear())
);
// 单元格事件
const onCalenderCellHover = ({ target }: MouseEvent) => (target as HTMLElement).classList.add('onhover');
const onCalenderCellUnhover = ({ target }: MouseEvent) => (target as HTMLElement).classList.remove('onhover');
const onCalenderCellClick = (e: MouseEvent) => {
if (!props.selectable) return;
let strValue = (e.target as HTMLElement).dataset.date as string;
if (strValue === undefined) return;
let value = new Date(strValue);
if (props.multiple) {
let d = (data.value as Date[]).map((v) => v.toLocaleDateString('en-CA'));
let ind = d.findIndex((v) => v === strValue);
if (ind === -1) d.push(strValue);
else d.splice(ind, 1);
onDataChange(d);
} }
// 这里阻止了点击取消选中需要通过tag的x来取消
.calender { else (data.value?.toLocaleDateString('en-CA') === strValue ? {} : onDataChange(value));
};
// 选择回显
const handleTagClose = (d: Date) => {
let strValue = d.toLocaleDateString('en-CA');
if (props.multiple) {
let d = (data.value as Date[]).map((v) => v.toLocaleDateString('en-CA'));
d.splice(d.findIndex((v) => v === strValue), 1);
onDataChange(d);
}
else onDataChange(null);
};
// 节假日
const holidays = new Holidays('CN');
const yearHolidays = computed(() => {
let h = holidays.getHolidays(currentCalendarDate.value.getFullYear());
let proce_h: { [key: string]: string } = {};
let _h: string[] = [];
for (let i of h) {
let d = i.date.split(' ')[0];
let hn = i.name.split(' ')[0];
if (_h.includes(hn)) continue;
proce_h[d] = hn;
_h.push(hn);
}
return proce_h
});
// fs-crud部分
const data = ref<any>();
const emit = defineEmits(['update:modelValue', 'onSave', 'onClose', 'onClosed']);
watch(
() => props.modelValue,
(val) => {
if (val === undefined) data.value = props.multiple ? [] : null;
else data.value = props.multiple ? (val as Date[]).map((v: Date) => new Date(v)) : val;
},
{ immediate: true }
);
const { ui } = useUi();
const formValidator = ui.formItem.injectFormItemContext();
const onDataChange = (value: any) => {
emit('update:modelValue', value);
formValidator.onChange();
formValidator.onBlur();
};
const reset = () => { // 重置日历
currentCalendarDate.value = new Date();
onDataChange(props.multiple ? [] : null);
};
const clear = () => onDataChange(props.multiple ? [] : null); // 清空数据
defineExpose({
data,
onDataChange,
reset,
clear,
showHoliday,
showDetailedHoliday,
showJieQi,
showLunarHoliday
});
</script>
<style lang="scss" scoped>
.selected-show {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.controls {
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
}
.calender {
position: relative;
width: 100%;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: thin;
.watermark {
position: absolute;
font-size: 16px;
transform: scale(10);
color: #aaa;
opacity: 0.1;
pointer-events: none;
}
table {
position: relative; position: relative;
width: 100%; }
overflow-x: auto;
overflow-y: hidden; .calender-header {
scrollbar-width: thin; padding: 16px 0;
}
.watermark {
position: absolute; .calender-td {
font-size: 16px; border: 1px solid #eee;
transform: scale(10); width: calc(100% / 7);
color: #aaa; }
opacity: 0.1;
pointer-events: none; .calender-cell {
min-height: 96px;
min-width: 100px;
box-sizing: border-box;
padding: 12px;
display: flex;
flex-direction: column;
justify-content: space-between;
cursor: pointer;
&.today {
color: var(--el-color-warning) !important;
background-color: var(--el-color-warning-light-9) !important;
} }
table { &.onhover {
position: relative; color: var(--el-color-primary);
background-color: var(--el-color-primary-light-9);
} }
.calender-header { &.disabled {
padding: 16px 0; cursor: not-allowed;
color: #bbb;
background: none;
} }
.calender-td { &.no-current-month {
border: 1px solid #eee; color: #bbb;
width: calc(100% / 7);
} }
.calender-cell { &.selected {
min-height: 96px; color: var(--el-color-primary);
min-width: 100px; background-color: var(--el-color-primary-light-9);
box-sizing: border-box; }
padding: 12px;
display: flex; .calender-cell-line {
flex-direction: column; min-height: 0px;
justify-content: space-between;
cursor: pointer; &.calender-cell-header {
display: flex;
&.today { gap: 4px;
color: var(--el-color-warning) !important; align-items: center;
background-color: var(--el-color-warning-light-9) !important; pointer-events: none;
} }
&.onhover { &.calender-cell-body {
color: var(--el-color-primary); display: flex;
background-color: var(--el-color-primary-light-9); gap: 4px;
align-items: center;
} }
&.disabled { &.calender-cell-footer {
cursor: not-allowed; display: flex;
color: #bbb; justify-content: space-between;
background: none; pointer-events: none;
}
&.no-current-month {
color: #bbb;
}
&.selected {
color: var(--el-color-primary);
background-color: var(--el-color-primary-light-9);
}
.calender-cell-line {
min-height: 0px;
&.calender-cell-header {
display: flex;
gap: 4px;
align-items: center;
pointer-events: none;
}
&.calender-cell-body {
display: flex;
gap: 4px;
align-items: center;
}
&.calender-cell-footer {
display: flex;
justify-content: space-between;
pointer-events: none;
}
} }
} }
} }
</style> }
</style>

View File

@@ -91,7 +91,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
rowHandle: { rowHandle: {
//固定右侧 //固定右侧
fixed: 'right', fixed: 'right',
width: 200, width: 220,
buttons: { buttons: {
view: { view: {
show: false, show: false,